From 1e5f4a360d69b08c83c748719e02ba3ff8c54c31 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Wed, 20 Sep 2023 14:28:48 -0400 Subject: [PATCH 001/155] Use mappings versions instead of node versions in mappings upgrade service (#99668) * Use mappings version number in mapping updates * Deprecate methods using node Version --- .../indices/TestSystemIndexDescriptor.java | 11 ++- .../elasticsearch/cluster/ClusterState.java | 6 ++ .../SystemIndexMappingUpdateService.java | 35 +++++----- .../cluster/ClusterStateTests.java | 67 +++++++++++++++++++ .../SystemIndexMappingUpdateServiceTests.java | 17 +++-- 5 files changed, 109 insertions(+), 27 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java index 0a090fa889a29..79c6dca764250 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/TestSystemIndexDescriptor.java @@ -37,6 +37,8 @@ public class TestSystemIndexDescriptor extends SystemIndexDescriptor { .put(IndexMetadata.INDEX_AUTO_EXPAND_REPLICAS_SETTING.getKey(), "0-1") .put(IndexMetadata.SETTING_PRIORITY, Integer.MAX_VALUE) .build(); + private static final int NEW_MAPPINGS_VERSION = 1; + private static final int OLD_MAPPINGS_VERSION = 0; TestSystemIndexDescriptor() { super( @@ -90,6 +92,11 @@ public String getMappings() { return useNewMappings.get() ? getNewMappings() : getOldMappings(); } + @Override + public MappingsVersion getMappingsVersion() { + return useNewMappings.get() ? new MappingsVersion(NEW_MAPPINGS_VERSION, 0) : new MappingsVersion(OLD_MAPPINGS_VERSION, 0); + } + public static String getOldMappings() { try { final XContentBuilder builder = jsonBuilder(); @@ -97,7 +104,7 @@ public static String getOldMappings() { builder.startObject(); { builder.startObject("_meta"); - builder.field(SystemIndexDescriptor.VERSION_META_KEY, 0); + builder.field(SystemIndexDescriptor.VERSION_META_KEY, OLD_MAPPINGS_VERSION); builder.field("version", Version.CURRENT.previousMajor().toString()); builder.endObject(); @@ -124,7 +131,7 @@ public static String getNewMappings() { builder.startObject(); { builder.startObject("_meta"); - builder.field(SystemIndexDescriptor.VERSION_META_KEY, 1); + builder.field(SystemIndexDescriptor.VERSION_META_KEY, NEW_MAPPINGS_VERSION); builder.field("version", Version.CURRENT.toString()); builder.endObject(); diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java index 95409b5bbf357..aa1fba7aecc81 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -284,6 +284,12 @@ public Map compatibilityVersions() { return this.compatibilityVersions; } + public boolean hasMixedSystemIndexVersions() { + return compatibilityVersions.values() + .stream() + .anyMatch(e -> e.systemIndexMappingsVersion().equals(minVersions.systemIndexMappingsVersion()) == false); + } + public TransportVersion getMinTransportVersion() { return this.minVersions.transportVersion(); } diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndexMappingUpdateService.java b/server/src/main/java/org/elasticsearch/indices/SystemIndexMappingUpdateService.java index d1f8acfccc0ac..a0667db91daf6 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndexMappingUpdateService.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndexMappingUpdateService.java @@ -12,7 +12,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchParseException; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.support.RefCountingRunnable; @@ -93,7 +92,7 @@ public void clusterChanged(ClusterChangedEvent event) { } // if we're in a mixed-version cluster, exit - if (state.nodes().getMaxNodeVersion().after(state.nodes().getSmallestNonClientNodeVersion())) { + if (state.hasMixedSystemIndexVersions()) { logger.debug("Skipping system indices up-to-date check as cluster has mixed versions"); return; } @@ -267,13 +266,13 @@ private static boolean checkIndexMappingUpToDate(SystemIndexDescriptor descripto return false; } - return Version.CURRENT.onOrBefore(readMappingVersion(descriptor, mappingMetadata)); + return descriptor.getMappingsVersion().version() <= readMappingVersion(descriptor, mappingMetadata); } /** * Fetches the mapping version from an index's mapping's `_meta` info. */ - private static Version readMappingVersion(SystemIndexDescriptor descriptor, MappingMetadata mappingMetadata) { + private static int readMappingVersion(SystemIndexDescriptor descriptor, MappingMetadata mappingMetadata) { final String indexName = descriptor.getPrimaryIndex(); try { @SuppressWarnings("unchecked") @@ -286,28 +285,28 @@ private static Version readMappingVersion(SystemIndexDescriptor descriptor, Mapp ); // This can happen with old system indices, such as .watches, which were created before we had the convention of // storing a version under `_meta.` We should just replace the template to be sure. - return Version.V_EMPTY; + return -1; } - final Object rawVersion = meta.get(descriptor.getMappingsNodeVersionMetaKey()); - if (rawVersion instanceof Integer) { - // This can happen with old system indices, such as .tasks, which were created before we used an Elasticsearch - // version here. We should just replace the template to be sure. - return Version.V_EMPTY; - } - final String versionString = rawVersion != null ? rawVersion.toString() : null; - if (versionString == null) { + final Object rawVersion = meta.get(SystemIndexDescriptor.VERSION_META_KEY); + if (rawVersion == null) { logger.warn( "No value found in mappings for [_meta.{}], assuming mappings update required", - descriptor.getMappingsNodeVersionMetaKey() + SystemIndexDescriptor.VERSION_META_KEY + ); + return -1; + } + if (rawVersion instanceof Integer == false) { + logger.warn( + "Value in [_meta.{}] was not an integer, assuming mappings update required", + SystemIndexDescriptor.VERSION_META_KEY ); - // If we called `Version.fromString(null)`, it would return `Version.CURRENT` and we wouldn't update the mappings - return Version.V_EMPTY; + return -1; } - return Version.fromString(versionString); + return (int) rawVersion; } catch (ElasticsearchParseException | IllegalArgumentException e) { logger.error(() -> "Cannot parse the mapping for index [" + indexName + "]", e); - return Version.V_EMPTY; + return -1; } } diff --git a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java index 21f8091f65dd4..46c6d1db47a7c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java @@ -1176,6 +1176,73 @@ public void testGetMinTransportVersion() throws IOException { ); } + public void testHasMixedSystemIndexVersions() throws IOException { + // equal mappings versions + { + var builder = ClusterState.builder(buildClusterState()); + builder.compatibilityVersions( + Map.of( + "node1", + new CompatibilityVersions( + TransportVersion.current(), + Map.of(".system-index", new SystemIndexDescriptor.MappingsVersion(1, 0)) + ), + "node2", + new CompatibilityVersions( + TransportVersion.current(), + Map.of(".system-index", new SystemIndexDescriptor.MappingsVersion(1, 0)) + ) + ) + ); + assertFalse(builder.build().hasMixedSystemIndexVersions()); + } + + // unequal mappings versions + { + var builder = ClusterState.builder(buildClusterState()); + builder.compatibilityVersions( + Map.of( + "node1", + new CompatibilityVersions( + TransportVersion.current(), + Map.of(".system-index", new SystemIndexDescriptor.MappingsVersion(1, 0)) + ), + "node2", + new CompatibilityVersions( + TransportVersion.current(), + Map.of(".system-index", new SystemIndexDescriptor.MappingsVersion(2, 0)) + ) + ) + ); + assertTrue(builder.build().hasMixedSystemIndexVersions()); + } + + // one node has a mappings version that the other is missing + { + var builder = ClusterState.builder(buildClusterState()); + builder.compatibilityVersions( + Map.of( + "node1", + new CompatibilityVersions( + TransportVersion.current(), + Map.of( + ".system-index", + new SystemIndexDescriptor.MappingsVersion(1, 0), + ".another-system-index", + new SystemIndexDescriptor.MappingsVersion(1, 0) + ) + ), + "node2", + new CompatibilityVersions( + TransportVersion.current(), + Map.of(".system-index", new SystemIndexDescriptor.MappingsVersion(1, 0)) + ) + ) + ); + assertTrue(builder.build().hasMixedSystemIndexVersions()); + } + } + public static int expectedChunkCount(ToXContent.Params params, ClusterState clusterState) { final var metrics = ClusterState.Metric.parseString(params.param("metric", "_all"), true); diff --git a/server/src/test/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceTests.java b/server/src/test/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceTests.java index b3492b203d354..925fadd511a79 100644 --- a/server/src/test/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceTests.java @@ -207,13 +207,16 @@ public void testManagerSkipsIndicesWithUpToDateMappings() { ); } + // TODO[wrb]: add test where we have the old mappings version but not the new one + // Is this where we "placeholder" a "distant future" version string? + /** * Check that the manager will try to upgrade indices where their mappings are out-of-date. */ public void testManagerProcessesIndicesWithOutdatedMappings() { assertThat( SystemIndexMappingUpdateService.getUpgradeStatus( - markShardsAvailable(createClusterState(Strings.toString(getMappings("1.0.0")))), + markShardsAvailable(createClusterState(Strings.toString(getMappings("1.0.0", 4)))), DESCRIPTOR ), equalTo(UpgradeStatus.NEEDS_MAPPINGS_UPDATE) @@ -239,7 +242,7 @@ public void testManagerProcessesIndicesWithNullMetadata() { public void testManagerProcessesIndicesWithNullVersionMetadata() { assertThat( SystemIndexMappingUpdateService.getUpgradeStatus( - markShardsAvailable(createClusterState(Strings.toString(getMappings((String) null)))), + markShardsAvailable(createClusterState(Strings.toString(getMappings((String) null, null)))), DESCRIPTOR ), equalTo(UpgradeStatus.NEEDS_MAPPINGS_UPDATE) @@ -253,7 +256,7 @@ public void testManagerSubmitsPutRequest() { SystemIndices systemIndices = new SystemIndices(List.of(FEATURE)); SystemIndexMappingUpdateService manager = new SystemIndexMappingUpdateService(systemIndices, client); - manager.clusterChanged(event(markShardsAvailable(createClusterState(Strings.toString(getMappings("1.0.0")))))); + manager.clusterChanged(event(markShardsAvailable(createClusterState(Strings.toString(getMappings("1.0.0", 4)))))); verify(client, times(1)).execute(any(PutMappingAction.class), any(PutMappingRequest.class), any()); } @@ -405,13 +408,13 @@ private static Settings getSettings() { } private static XContentBuilder getMappings() { - return getMappings(Version.CURRENT.toString()); + return getMappings(Version.CURRENT.toString(), 6); } - private static XContentBuilder getMappings(String version) { + private static XContentBuilder getMappings(String nodeVersion, Integer mappingsVersion) { return getMappings(builder -> builder.object("_meta", meta -> { - meta.field("version", version); - meta.field(SystemIndexDescriptor.VERSION_META_KEY, 5); + meta.field("version", nodeVersion); + meta.field(SystemIndexDescriptor.VERSION_META_KEY, mappingsVersion); })); } From ddf17e6be536047c06f04babe949bcc115330fdf Mon Sep 17 00:00:00 2001 From: Mayya Sharipova Date: Wed, 20 Sep 2023 15:43:40 -0400 Subject: [PATCH 002/155] Increase the max vector dims to 4096 (#99682) --- docs/changelog/99682.yaml | 5 ++++ .../mapping/types/dense-vector.asciidoc | 4 ++-- .../60_dense_vector_dynamic_mapping.yml | 4 ++-- .../vectors/DenseVectorFieldMapper.java | 2 +- .../script/field/vectors/DenseVector.java | 2 +- .../search/KnnSearchSingleNodeTests.java | 10 ++++---- .../vectors/DenseVectorFieldMapperTests.java | 20 ++++++++-------- .../vectors/DenseVectorFieldTypeTests.java | 24 +++++++++---------- 8 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 docs/changelog/99682.yaml diff --git a/docs/changelog/99682.yaml b/docs/changelog/99682.yaml new file mode 100644 index 0000000000000..48e99a5145674 --- /dev/null +++ b/docs/changelog/99682.yaml @@ -0,0 +1,5 @@ +pr: 99682 +summary: Increase the max vector dims to 4096 +area: Vector Search +type: enhancement +issues: [] diff --git a/docs/reference/mapping/types/dense-vector.asciidoc b/docs/reference/mapping/types/dense-vector.asciidoc index fb50ee36644a6..76c9313374b13 100644 --- a/docs/reference/mapping/types/dense-vector.asciidoc +++ b/docs/reference/mapping/types/dense-vector.asciidoc @@ -62,7 +62,7 @@ In many cases, a brute-force kNN search is not efficient enough. For this reason, the `dense_vector` type supports indexing vectors into a specialized data structure to support fast kNN retrieval through the <> in the search API -Unmapped array fields of float elements with size between 128 and 2048 are dynamically mapped as `dense_vector` with a default similariy of `cosine`. +Unmapped array fields of float elements with size between 128 and 4096 are dynamically mapped as `dense_vector` with a default similariy of `cosine`. You can override the default similarity by explicitly mapping the field as `dense_vector` with the desired similarity. Indexing is enabled by default for dense vector fields. @@ -132,7 +132,7 @@ integer values between -128 to 127, inclusive for both indexing and searching. `dims`:: (Optional, integer) -Number of vector dimensions. Can't exceed `2048`. If `dims` is not specified, +Number of vector dimensions. Can't exceed `4096`. If `dims` is not specified, it will be set to the length of the first vector added to the field. `index`:: diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml index 767e898792f20..030ce2e2332b1 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml @@ -30,7 +30,7 @@ setup: refresh: true body: my_field: [ - -457.1953,259.6788,271.9127,-26.8833,403.0915,-56.9197,-445.8869,-108.8167,417.8988,13.4232,-281.765,-405.8573,262.7831,-279.493,328.5591,-453.3941,-116.0368,435.4734,-439.0927,-332.9565,355.4955,324.9878,33.3519,-165.0182,188.1811,467.3455,185.1057,-233.8598,-17.6827,283.4271,-329.1247,-402.9721,404.7866,-358.7031,-267.4074,441.8363,320.2389,-128.0179,339.544,196.2018,-60.2688,336.0228,-440.1943,318.6882,-158.2596,277.0925,-487.4971,-338.9865,-275.716,136.8547,-253.6206,-40.2807,-357.0971,188.0344,-203.0674,449.9618,-223.2508,468.1441,302.4002,-65.0044,342.4431,205.6774,-118.636,-29.9706,183.9825,223.956,314.0691,137.0129,-8.0452,-15.131,-269.8643,-12.691,228.9777,-147.8384,-347.1117,-283.1905,459.2004,296.1321,-483.1799,414.3423,383.0187,-408.5525,-286.8169,482.5853,9.5232,-459.4968,-333.2521,109.0969,129.5107,43.4369,455.8283,-4.0423,-318.5019,339.1641,416.3581,-309.0429,84.2325,-355.8753,264.7671,43.8922,-298.6039,412.4413,19.4198,-251.279,-191.157,-478.2058,251.5709,-178.9633,479.293,188.399,380.9755,268.6575,120.3467,-322.0305,-255.4894,-377.515,56.9153,-133.9486,156.2546,-428.9581,-54.994,28.2146,158.7121,-426.7307,491.0086,-150.7205,-233.1005,244.5174,45.911,-406.1181,233.1636,175.9334,414.2805,421.7396,-322.8029,-252.2412,35.7622,318.5223,-141.5121,-375.4407,380.3081,222.1228,443.7844,367.377,-202.9594,-493.6231,-184.2242,-253.9838,463.1952,-416.3887,252.0867,-63.5317,411.0727,98.6261,330.7369,363.5685,-498.1848,-413.7246,-2.5996,-238.3547,-355.6041,-303.698,43.6266,383.1105,-72.3066,274.7491,321.9322,220.9543,-30.5578,400.0891,-181.7069,-386.4403,497.2206,-408.9611,138.485,-133.5666,-340.2569,-223.6313,270.884,-215.9399,74.3931,-244.1364,353.4219,-156.9905,488.3148,96.352,401.8525,-468.8344,129.9715,-27.1953,-168.631,187.7049,-336.5255,331.0652,204.3538,36.0182,366.8502,-468.6579,478.1409,-332.6136,-281.8499,63.7165,-458.8161,14.8894,-145.6397,267.1499,85.2025,326.3764,-419.6361,-133.9626,102.0618,443.3099,-207.9032,132.7032,234.001,-26.0754,105.6478,174.1252,-403.3511,-164.9714,-262.9344,-58.9668,357.6414,355.7508,-331.8443,153.5733,417.5712,260.7394,-150.1053,-435.6525,-364.1558,328.6183,-270.0863,107.1746,345.7998,480.8749,206.3896,-498.237,495.0835,481.9384,418.5571,-246.5213,-363.7304,311.7076,-53.1664,-297.3839,122.3105,-13.9226,-145.9754,-189.1748,460.9375,194.5417,-28.1346,-261.2177,-88.8396,-254.6407,-465.3148,-169.5377,24.3113,-116.2323,-420.3526,317.2107,-231.6227,-270.8239,387.8598,412.4251,428.1373,308.2044,275.2082,402.3663,-209.9843,-492.7269,225.1948,326.469,207.3557,-131.7677,371.9408,-139.3098,324.205,-126.6204,-335.0853,-248.2587,-344.907,307.2109,-441.3296,-318.027,414.6535,172.0537,-280.4991,331.0475,-158.0178,-285.1951,12.3632,149.9347,282.8302,-91.5624,-180.6097,496.0881,368.2567,357.6875,-194.2106,48.9213,-479.2956,-165.139,238.7811,302.7007,297.2805,208.7099,-5.5755,-85.7911,-358.1111,344.6131,415.7199,-219.1525,490.5003,-46.0096,498.2818,-91.8067,384.0104,396.1107,408.2827,-5.3919,-333.7992,-168.985,273.72,359.7125,227.7621,158.3406,-366.9722,3.7709,27.2728,71.9754,269.5792,-365.281,117.9152,-184.3682,356.9013,-142.6579,-496.7598,122.0194,89.1247,4.1914,-81.9905,465.0841,115.4727,169.6116,-199.9951,-223.3149,-447.3022,11.831,320.2368,105.1316,344.2462,8.6333,62.2285,-70.3944,-284.6694,-482.4229,-448.1569,-237.7858,222.3921,-172.1386,-312.5756,-390.0565,398.951,119.9784,-419.6537,121.3186,481.3011,-181.6662,-56.0219,424.1359,7.1461,138.8567,-307.0606,334.066,254.0897,473.7227,45.5936,133.7268,49.5334,-283.3406,179.4466,105.6191,-30.4162,271.5774,6.1156,110.4732,286.4325,13.3431,494.0139,-371.7624,283.3652,272.0558,-302.343,122.7245,-463.9261,299.9807,282.4502,-262.4911,183.4289,222.7474,-229.5973,141.6188,262.5468,278.1155,-331.0891,-393.6027,-230.1461,201.6657,-93.3604,-395.8877,-125.2013,-222.973,368.3759,234.6628,-28.6809,-151.0703,432.0315,253.1214,430.7065,-143.6963,499.84,85.1683,280.4354,196.6013,139.0476,120.8148,-398.8155,-335.5504,229.0516,403.8604,-383.9868,-79.975,-152.77,220.4036,135.0355,238.2176,-242.3085,-177.0743,381.8202,411.167,378.0153,456.5976,364.013,24.2316,-395.4659,-210.2581,138.7539,479.7398,-291.7797,-123.0491,188.9817,42.8931,-354.4479,358.853,-43.6168,-190.6656,-103.3037,47.8915,-358.5402,374.9758,493.9951,-427.2376,-119.1142,-453.2975,-326.2696,-212.8273,-142.2931,-179.795,355.77,-156.2903,331.2006,451.9252,185.2944,-96.1941,173.0447,345.2744,43.0151,381.7845,-143.4125,84.654,-208.7053,-293.141,333.6349,-80.472,-376.9817,214.6298,-43.0931,-254.7834,-421.6961,-368.844,467.5544,-418.61,-66.6824,-350.2671,348.8241,252.3495,41.8677,-128.869,90.0391,-136.7405,-136.7822,489.8074,-396.8204,63.8355,323.9557,-83.6674,451.263,152.8955,-291.7497,410.0787,-299.7468,51.34,-298.6066,-58.853,325.911,-281.9541,-15.3457,299.1325,-347.4959,388.407,343.1096,28.1816,24.3013,-111.3312,190.5583,279.9848,-479.8894,123.2182,233.8425,-466.2128,-134.7122,217.8674,432.9523,-186.799,-477.2512,-223.5514,64.274,141.5251,-161.2187,150.2791,-228.1087,81.172,451.0879,-230.3818,-304.9398,402.1081,199.1266,275.3423,-123.9548,-21.1815,-384.544,446.9626,208.9692,-337.4827,-58.1011,344.2642,230.2868,44.9176,245.9885,-284.1875,-351.6104,108.1289,459.649,191.4334,53.591,136.7139,10.5912,-15.8411,62.8305,448.5256,194.7705,-356.3214,84.4996,-133.2502,-358.6308,262.7949,219.8741,-355.3985,468.2922,243.7227,-408.3166,188.6111,-221.7264,-286.8234,-340.3046,-224.5375,332.2615,73.2788,-24.7857,-485.2204,-136.7196,-162.9693,92.6017,-99.611,-186.5203,495.5483,240.8051,409.6493,-58.1321,-154.1239,-335.9719,-82.4408,-471.3057,-43.373,301.0884,-96.6359,-236.6906,435.7313,-227.7263,-406.8904,-392.3187,169.0043,-371.0852,-271.3652,-57.4466,-196.8455,52.741,361.7395,-117.8599,190.5339,276.6457,-321.9851,425.881,-473.2662,-74.2968,221.3612,-465.4429,181.723,-78.4508,21.6152,148.8107,-166.1687,-281.6391,-462.3636,-420.5255,-161.4143,98.8383,-374.5345,-366.2851,187.1506,-405.1865,239.4847,-246.8352,33.1748,-344.1211,477.9759,-294.1354,-359.5015,-44.8454,151.7072,-22.7324,-260.3293,99.1414,-20.5536,173.3766,-422.6692,458.3853,-199.7898,-236.3929,365.2599,-66.4191,388.3472,283.0336,-268.9463,269.5704,360.9679,-322.102,-407.0705,-93.0994,338.9108,-189.1359,-216.9102,-249.0153,122.6058,-254.8318,-112.2771,-279.0506,-168.4431,392.888,394.7607,468.0544,340.1852,-293.1288,-8.2912,-419.2608,323.3382,-93.8793,-242.0672,427.7716,-441.6906,128.3229,424.4679,-71.8586,134.5411,-74.5205,18.4141,17.7277,126.9123,-137.6119,33.3783,222.9912,-279.3582,89.1226,-90.031,12.7221,98.7767,-80.2372,-485.9212,-481.6575,-325.9729,318.8005,-433.786,-296.6337,421.6515,-27.2786,-445.2456,451.8876,-482.1014,-143.1098,186.1258,-90.2432,-297.7479,-351.0026,-423.7518,-219.6096,-269.2043,33.5767,-325.4335,392.4866,-418.243,112.5852,-248.1306,451.2154,-419.2995,154.5752,483.6323,-315.962,-196.872,406.1769,-356.9868,67.5251,-255.6475,103.5181,-450.4418,386.9518,456.4057,99.4591,-166.636,275.5374,200.4925,99.7623,292.6794,-422.3998,419.4837,-466.548,-462.8519,-381.4489,472.8356,-129.9563,441.4941,-376.1232,-114.1945,233.5531,313.6963,394.9503,-278.7558,350.7515,47.9427,220.7074,-178.9789,-346.0485,-128.5665,8.9461,159.9838,-57.3637,351.9478,-65.9411,-258.1788,498.9494,-472.613,-428.5678,17.3981,-435.3682,-421.155,-54.9177,-490.2348,178.3777,-31.9618,-242.1805,362.3736,380.8179,446.4272,-23.9142,61.3588,-489.5704,363.6446,-186.1519,-351.8684,-322.2791,-226.0431,404.6996,203.9824,306.0958,234.0145,-180.4996,452.0633,257.171,-83.6197,-393.152,396.6934,32.156,-428.7645,183.7886,494.767,68.3905,278.9785,-40.4759,261.7298,236.5778,4.5577,-130.9582,433.2837,-298.1139,-107.9822,-196.8446,-121.1765,-292.5509,-246.4546,-258.6038,280.1334,-52.6511,483.2928,-185.7577,-75.3705,351.3411,179.1282,-479.3838,166.2733,-197.9043,282.6848,-50.4744,-492.7178,183.6435,-127.2379,483.646,433.0805,-228.5488,139.8314,-145.1337,-403.1749,306.2704,122.7149,479.6928,85.3866,108.095,-224.152,494.6848,-368.4504,-180.7579,61.7136,51.2045,-383.0103,-376.4816,-292.8217,-201.118,332.1516,425.2758,138.1284,-229.4302,432.9081,2.9898,-437.7631,-448.2151,129.9126,-170.2405,499.0396,-48.2137,363.8046,-423.2511,-28.0804,-267.826,-356.6288,-99.9371,-409.8465,170.4902,-269.2584,-277.4098,300.8819,-142.5889,339.0952,16.2275,-310.8646,201.0733,-495.5905,341.9279,-149.1184,-494.4928,-81.7343,209.9762,273.4892,380.3163,359.2424,-242.5,-42.1268,-303.9792,11.6018,361.5483,416.4178,10.3282,195.9796,148.8096,-60.9724,-205.5221,-145.4574,-341.5913,426.8996,-19.5843,60.6265,-133.4191,-139.8737,281.7465,461.2854,-270.8902,61.0182,-58.6791,-254.0193,-234.1206,-208.7334,39.7498,-14.337,-68.2319,-342.2756,403.6834,401.6122,-166.1637,47.3592,-325.7,274.5459,343.4873,328.3783,-370.1657,-122.8967,-231.3182,122.6609,119.2685,-223.5437,-210.8076,116.5022,340.2814,256.1852,-217.3487,-150.9598,331.1343,-453.8182,-448.0842,-95.2475,-340.9942,-416.7835,-96.7226,-328.7212,-373.4337,472.2214,-484.522,-465.1583,330.0712,73.2052,-55.1266,-352.8984,341.0742,-230.4845,321.0752,236.2116,35.1902,75.3489,-469.4042,110.2036,35.1156,454.7224,103.0685,-221.7499,-23.6898,-259.2362,-110.509,-261.0039,219.2391,-139.9404,155.7723,377.9713,434.0318,-365.1397,459.1471,-318.5774,323.4256,194.325,-311.9529,-153.9019,-346.5811,76.4069,443.2121,-199.407,495.6636,-138.5213,-145.3432,-151.7758,-365.3547,263.6507,-491.1686,-183.5585,-12.6044,318.5346,-443.8639,-179.0338,477.9093,-355.5118,-423.0035,-229.1166,-96.7782,-479.2384,192.9085,223.3407,-302.9472,297.3847,477.584,-297.5958,168.6023,-80.6912,-89.8717,87.1476,-129.7807,346.5576,-253.9729,-399.6858,-389.5785,35.1648,-180.451,-49.6084,83.9582,-185.2329,97.283,195.5249,-91.6969,199.202,-449.792,333.4825,-113.7558,443.434,394.3587,-94.9074,71.2092,-251.1774,-85.047,-46.4004,20.2595,341.1073,-91.2527,86.3775,303.1247,-336.9011,343.9894,-384.1261,154.4411,-465.2493,-63.3249,488.0231,348.6725,458.2093,322.401,220.2532,283.3734,-386.4252,-256.5262,-87.2205,96.8199,47.6908,-399.6307,214.7716,-19.9177,-458.513,-194.3218,-320.5342,-275.857,-301.6955,-84.9038,358.3475,-88.9271,499.7721,-161.7403,355.4894,313.6211,-176.1703,61.8427,107.603,-176.063,-426.5408,292.3612,58.3331,-115.8853,471.4131,-76.4815,-309.6263,361.4518,192.4763,-145.7968,256.3888,133.335,-474.0901,-366.9793,-495.223,457.2366,170.056,285.0152,89.8213,225.2251,354.1822,-298.374,-332.9164,-55.2409,306.9283,25.9392,218.0624,7.5085,-151.8768,-155.4932,6.0001,201.4506,-259.9874,485.1078,-362.8516,-230.1434,-398.2512,243.0012,32.302,-197.91,144.1195,-89.4196,-44.0399,-371.7866,227.6007,492.7526,499.3824,162.2475,279.0325,177.0781,341.0137,199.6009,108.1678,312.2319,-211.5001,-92.675,357.0513,-337.924,-348.984,-350.3677,173.3473,-193.7346,-318.5609,-2.0928,46.6287,-346.8513,36.634,-277.4949,-149.325,481.1378,370.3864,-139.6689,-332.2805,48.0292,109.8363,494.6994,373.6992,495.7442,400.4998,-26.2276,-308.7669,188.9497,257.9182,-116.6944,269.8932,197.005,123.1139,-356.2058,485.1982,-4.0119,397.8434,-204.67,-494.5133,-414.1299,142.1512,-36.5446,390.0718,6.9876,263.1216,457.5598,89.6086,-266.3804,17.3457,88.8182,236.6271,81.175,-170.2249,-5.7664,422.7852,180.3349,-135.2642,149.2285,-70.6607,-46.169,-389.3313,230.6125,388.4853,-438.3426,111.8034,300.0416,37.5604,-437.3868,-114.1336,312.7777,-99.1161,-312.9015,-147.3787,-434.0536,19.5034,141.706,-281.4504,-208.9608,281.4619,-361.0596,-464.2757,77.8205,232.5575,165.4104,424.8738,124.5555,342.038,86.7543,278.0216,311.2686,337.834,-90.0545,-210.1143,-488.4095,-80.7535,92.3731,-122.622,-288.0571,1.7285,-5.2998,100.0717,-395.0571,-477.5587,-160.5642,-119.4214,-232.233,415.7276,-204.3216,-436.7766,-103.4644,-427.0939,-31.0927,-440.2919,120.5971,-223.3623,-199.0988,304.8697,432.5731,-231.5791,-397.696,306.4134,330.1018,32.4345,-175.719,464.6091,-291.5686,300.1631,-167.4592,238.9574,104.5893,-187.2215,-294.0111,-361.9094,480.6847,-304.2133,-448.7144,67.7235,-255.9669,254.7379,464.5465,6.8909,-368.7554,337.5993,39.1928,-376.0625,433.4224,-109.1488,341.7731,377.843,446.839,-192.283,251.1592,437.6812,-478.3409,345.7668,377.965,125.6188,-462.0904,-235.3324,316.8892,-460.7371,248.9306,418.7082,-333.7257,-104.5062,-408.1356,148.6624,-158.4929,-477.0664,80.4926,-214.6292,211.3377,322.7854,-312.851,403.0215,-213.3089,-71.3355,-276.1068,-293.0902,-277.4559,54.2176,-119.1285,-479.4361,-492.6072,8.3732,42.4988,-5.576,-198.6151,-357.0952,-331.5667,186.6195,317.3075,201.267,-37.1731,-278.3164,-467.7796,-163.3909,-117.305,-233.9266,277.7969,181.9723,178.8292,-168.7152,-436.041,171.345,369.0302,423.7144,434.0961,-428.1816,23.7334,-136.6735,-222.4486,180.8461,57.5968,129.2984,127.1866,-109.3928,-143.6253,-385.9948,127.9867,-8.8096,-239.844,66.6491,-50.7301,-309.1113,-474.6991,212.1767,-444.4596,-211.3601,351.3551,335.0507,-128.6226,-98.5249,-257.454,489.8014,-378.8622,311.0304,-4.9107,362.7586,-458.8825,373.2779,-103.29,-5.6216,122.0183,76.9731,17.8771,289.8893,-56.4338,375.9665,-83.9991,440.0823,142.2309,-471.0813,-59.4847,-400.4217,91.4892,374.4009,486.8697,414.5213,-0.3535,-278.2345,-231.206,-238.479,389.3143,-276.9742,-33.9869,349.1201,127.3928,-410.7213,337.3789,36.4048,333.4291,-12.4075,483.8778,311.4489,-74.0628,-379.6051,463.234,157.5614,-140.9455,120.7926,-161.2341,194.162,-412.6181,-9.1258,-194.5065,441.1572,255.5455,-73.8086,-119.4013,-486.4792,-27.4352,98.9738,-119.002,-75.5589,261.7675,156.0993,89.6457,-190.6318,429.9325,195.9536,-172.6155,-22.7976,438.9412,-246.4661,447.7281,434.5346,405.8957,217.3324,392.6129,-158.604,15.8632,483.0414,334.7693,-307.2482,302.1267,-7.4125,3.8081,-405.7316,377.5069,51.2307,235.0695,269.737,-389.3487,186.4225,-36.8521,401.2051,-59.0378,-190.8023,-182.8076,-362.6136,-124.8064,362.4142,45.3344,-330.1214,-162.5452,-434.4411,219.1143,-374.1038,364.5639,-268.582,-22.9247,-73.8849,-54.5258,-23.0882,167.9233,-181.9807,-207.1173,300.2193,206.5903,-72.013,-244.4396,-435.5389,10.3523,-435.3545,-138.8392,449.8426,-244.8971,229.7666,267.5225,-401.6021,466.3278,418.3623,-317.8205,28.5192,384.5628,-79.6177,469.4532,-395.1986,-353.4477,-93.6914,70.3999,-441.0627,-201.1221,141.2748,433.3389,82.413,-394.0046,-438.6836,453.4704,-160.6535,353.0374,-238.0377,236.5195,497.9019,202.9472,-421.6417,-382.042,84.6308,430.1599,-390.9918,-195.0401,255.6526,-86.5964,-491.667,-199.1557,-102.7114,474.877,-292.9154,-77.3163,143.5625,58.8126,-284.8908,-457.6457,212.5317,480.4032,-324.0829,491.0165,-494.7934,267.4311,-142.2401,-368.9058,-370.4955,498.803,-6.7377,-395.373,177.8868,306.9761,80.4185,-239.1253,-435.1349,7.6298,-157.6242,348.6095,475.7845,317.7116,-353.7336,-40.2881,353.7096,-60.9783,-385.5816,243.8071,-398.8341,62.343,340.0251,-24.8105,-343.4186,189.6737,-467.3026,104.7127,159.5467,-482.5496,71.6951,-163.5304,-321.8438,185.2875,-331.6885,-102.6817,-242.7548,-259.4407,220.6898,231.6571,-297.1145,-186.9472,-316.9286,-36.2392,-293.964,296.3878,467.7409,-277.6389,493.2143,417.1244,12.241,-343.7893,-33.7207,457.2978,-248.9726,-409.5439,-92.4779,-173.7584,400.8483,59.7439,13.3265,-175.617,37.333,-307.6469,-82.3687,332.578,-412.0079,144.7037,350.6506,423.3235,-53.2147,67.9581,-447.3845,-461.0187,371.1702,386.2045,352.2722,-119.098,123.9178,-52.0535,465.2626,474.0272,402.9961,491.4763,-33.1373,-228.8607,-383.3299,408.8192,-275.155,489.8633,-349.5073,346.9781,129.3929,282.1868,-77.3384,277.3026,412.3277,263.6705,473.3756,-437.9988,114.1686,-452.3331,-167.8898,-193.6217,444.6168,-354.3223,-238.0967,432.0883,-349.7249,-42.3659,-304.7343,296.2192,-136.5386,-121.7774,450.4678,140.5384,-450.8993,93.8942,-54.4945,498.521,-461.7182,111.5166,-397.6007,-397.959,-20.9331,-19.7068,78.551,161.9472,-24.8682,-434.4537,102.9447,214.298,-494.3813,211.6782,64.8196,372.6962,-399.8337,114.5476,-191.0045,-369.6465,-391.7201,-204.9951,-201.7654,475.898,-262.3247,-348.6974,79.4062,-112.4281,-102.266,67.3008,335.485,68.4289,-433.9104,-392.963,-73.3788,276.5766,-105.2219,422.6201,192.915,-388.3541,242.3915,479.5633,42.5998,259.6189,-316.5861,390.1121,-216.0274,-373.296,103.7169,321.9107,19.0023,487.2627,151.6922,276.7424,461.6928,24.4758,133.263,-47.289,-413.9538,435.2414,-466.9724,-270.6602,238.9442,-110.5389,403.5151,-395.4393,-208.2219,-53.0773,-26.5792,-387.6534,-120.5566,143.2237,-305.3778,442.0665,417.9523,460.3337,254.8689,-375.9436,-101.0153,232.4727,-35.5285,-470.3007,-423.9161,-108.9997,-29.6555,233.1043,240.4766,404.763,276.8465,-354.4058,74.0678,-343.244,332.9786,361.2964,-322.0828,-41.1861,-122.8074,-299.5682,-481.218,-157.3994,310.6317,-261.176,310.2644,-239.9855,255.1004,-311.3351,437.9486,78.1311,-133.9261,-176.2119,45.9943,492.3169,266.5795,16.8553,-470.9413,-331.2718,218.4122,369.7118,-179.3201,-165.7277,-87.9832,357.6499,-261.0345,442.1609,113.2997,-112.5643,481.2426,-365.4958,400.5374,-395.085,303.8103,-292.0268,167.0744,-199.013,174.9283,498.3585,-337.466,303.9078,-326.0901,-331.7143,6.7189,-277.1371,-204.9097,-313.4259,-462.7296,437.8485,267.2872,157.752,143.8784,60.1304,-492.991,326.0132,-123.3415,390.8461,-293.0175,483.4759,240.4338,271.6879,483.4801,391.2687,238.3995,-246.607,-411.7722,-257.9864,238.0949,494.3455,-489.0838,-26.7283,317.1161,-264.0242,-16.6819,-141.4839,429.101,252.2336,-325.1541,471.044,452.352,7.4546,343.3004,-336.4424,489.6317,307.1831,-139.2075,153.572,-332.5617,-361.892,110.6459,-384.8117,-423.0834,-277.9929,44.5303,167.9458,364.1204,-222.5008,-148.7923,198.4694,-74.0043,-458.4327,-227.5346,272.4441,-477.2587,303.1998,72.3129,112.9422,-98.2577,296.903,-489.0569,-461.4503,-381.6239,-440.6212,-354.1834,356.1583,-220.6533,192.5295,-409.0818,-264.2973,498.2192,-306.675,-313.6103,-124.9266,-436.5922,297.9051,121.9351,425.3888,-283.9925,-360.441,-347.4517,8.6814,477.4163,-344.6926,-311.574,-199.9541,-272.862,-360.8642,-306.0856,-218.9529,200.1938,-187.9337,-149.341,-431.5156,-135.3958,131.1299,262.0532,-210.162,353.4392,-249.2969,216.4223,499.6139,215.8176,-346.1569,177.2202,-173.1132,-466.9007,-310.9848,463.485,6.516,-334.8823,-282.7409,-375.2367,-127.4937,257.2427,384.9285,206.4053,-283.9167,369.6312,-325.1146,452.7523,-103.9792,-51.036,153.325,-344.1749,289.4824,109.8308,375.2284,-249.8481,367.8478,71.0143,471.6136,-265.6336,12.9061,-470.1288,-113.547,38.8925,-205.7232,418.6063,475.6095,-18.8731,-431.5545,-288.6452,-406.8928,79.4828,-152.1474,345.565,-200.8038,174.7789,379.2991,-385.1188,-217.6888,241.9077,-449.1824,467.832,186.0095,-82.8376,-450.7827,-32.2903,-288.132,169.8581,-275.3198,-388.1222,-431.3601,64.9652,368.9351,107.4999,408.8666,267.7858,-462.4349,-198.4615,378.1182,252.7529,-344.883,-364.0161,-124.6144,-222.8902,-103.7114,387.1701,-363.7944,-237.934,230.2082,-63.1276,-456.8188,361.9248,461.0643,160.8127,305.6079,81.2236,-322.0002,-273.4727,-356.9758,227.4751,278.5386,-10.8627,49.6988,-495.2527,428.0901,393.6169,-360.5547,-137.0244,26.962,-326.3379,-399.4972,449.7645,-238.7444,-69.8461,222.6126,-68.7657,132.7567,255.7355,-190.3762,271.6129,405.5764,115.8834,0.9645,331.1665,396.4585,217.4435,-323.6914,39.5915,282.4489,411.3888,-219.2131,240.8913,-109.5264,-438.3067,-157.3961,-180.7485,-258.9153,61.7008,483.4718,-386.0406,-499.1824,-90.2675,-358.5152,-79.3051,-97.4094,-91.7246,63.539,-307.0526,226.416,-454.475,-375.7449,300.532,409.7526,7.7042,-320.297,-244.9896,-282.6645,-414.9866,-331.4623,316.162,348.8361,-342.8609,477.2374,6.5636,-483.931,341.3556,498.2318,-46.3428,203.981,101.2793,128.4547,-285.068,56.5149,-407.6478,-151.4672,116.6673,-115.0498,-491.7974,-151.9475,474.7827,-288.4179,286.4447,-430.6331,-279.1458,318.721,-276.8375,157.9586,-9.2346,398.8374,380.2256,61.1557,13.0746,-80.139,-134.8798,-37.6466,-209.7381,236.1511,388.5629,-196.1123,-481.5887,327.8334,408.2074,479.1439,85.082,227.7623,250.2644,-47.8238,464.8471,-431.5099,489.9794,452.9999,-50.8695,-429.0862,-138.8555,-395.3346,391.3405,-249.4682,-280.6761,-460.5297,1.0129,199.1008,-97.4134,-235.0172,-466.1287,-302.7993,298.4108,-22.478,173.9936,122.8033,-235.0353,231.5057,-97.2265,-203.8224,457.6806,484.1385,-309.3619,-168.3588,-177.2797,-3.9408,-279.2997,104.4862,-139.4921,-450.2539,402.541,-437.1151,-337.4914,-200.3446,-164.484,-293.7216,471.7414,192.6153,233.1926,-122.8377,356.5476,450.1361,-400.0941,61.0466,441.7145,189.7192,-69.6348,252.5418,-246.5242,-344.0219,14.2904,87.2185,-119.2684,205.422,-374.4802,33.4042,81.2271,-2.5025,-138.6816,8.1989,-439.7698,-446.1887,-374.9012,160.9795,49.3705,72.7925,245.9454,-138.7558,11.9923,414.9421,5.9535,-142.9589,396.2571,-222.2068,-2.6172,-90.5871,346.7415,-337.3213,-372.4473,91.8271,310.6442,263.7468,-357.0433,-246.0827,25.4967,55.8069,-64.7183,-342.7375,-356.7083,70.0885,-79.026,-346.3906,206.2687,-440.6602,321.8775,223.3025,159.6939,292.4308,241.077,-219.0901,495.9946,0.3506,-166.4262,475.1836,-272.5527,118.8711,458.2456,353.3839,-82.5653,37.2834,-92.4387,146.5082,233.4743,-408.0537,-469.9263,148.8959,-324.352,498.608,-324.5319,-114.6779,-200.4192,404.8448,-289.7989,400.6151,-372.9065,359.7581,141.4237,-304.6837,314.3738,-302.4693,442.6138,-224.0818,270.1887,-477.1098,429.0239,264.1871,26.84,283.4518,129.5215,6.6673,-91.4464,75.821,261.5692,-403.0782,-213.9284,-356.8221,-232.4484,33.5696,99.1931,344.0097,187.4695,-264.0572,-199.6103,342.5485,187.058,31.5948,-275.4046,215.9846,425.1114,327.1992,437.8426,-281.2049,71.7953,393.346,-339.9023,-78.8502,314.1866,-120.7207,-416.0802,-327.1001,413.6143,-236.2051,247.1197,318.5011,-194.295,486.3421,409.0831,252.6212,-452.654,-215.7497,-464.1643,61.9033,66.4139,-425.8918,-401.3522,-395.1639,427.7052,-264.1728,131.9144,258.4416,-442.2357,68.3167,441.5518,138.4774,470.7538,-14.6434,-436.2225,385.0708,286.1155,323.9014,137.4596,-352.5503,1.9307,-314.7656,449.5639,-468.3008,81.2499,487.4562,270.1387,-445.3627,460.1174,-205.2539,-32.6044,359.0438,-115.5841,-268.6624,-495.8554,-474.4781,337.9834,-281.4488,252.1636,-33.645,-26.6636,193.8834,287.2377,6.9748,414.4343,-211.7143,-23.0035,-226.5275,-400.285,-336.3935,28.1908,244.27,21.9938,-222.3759,-103.1418,464.7943,-256.0156,46.7511,-487.2509,-321.3631,479.2142,328.166,-481.2039,253.4962,100.2875,-399.98,-81.5868,289.7597,-318.7266,-264.2078,129.4063,407.6828,222.8346,370.0391,46.9838,-356.4992,-305.9992,-258.4048,-410.7736,-245.9092,32.9185,-237.9085,-403.8853,12.0239,-164.6252,107.369,8.0379,-139.3796,365.9266,-448.5863,314.1141,-280.0686,-463.4747,2.6092,-376.8811,96.7462,242.419,-480.9968,345.3697,328.281,39.0387,-342.3026,469.0461,-103.9411,381.0458,-141.6771,-4.7988,289.4799,-55.0671,-292.4788,364.1267,-395.9876,-232.5859,-285.7012,-444.7762,79.5454,251.5539,359.3705,467.2154,273.1778,-373.8216,299.611,-464.32,-106.0638,491.2626,-39.3721,-110.1154,383.4063,45.0848,262.2361,-111.754,249.0826,-305.9751,22.9663,-120.4794,484.0797,151.9063,388.5088,105.9067,444.0361,-45.5696,243.9313,303.4003,-27.795,-7.2151,411.6561,-100.6193,-207.3277,-6.4576,-300.3722,118.2638,342.3654,66.7861,104.0615,180.5752,281.6788,-342.7549,-65.8778,140.9091,-169.8935,-437.2435,-392.4147,-348.2217,202.3684,440.4071,-276.2247,129.5096,-43.4059,-456.876,-445.1126,-193.8847,-156.3408,274.7116,-129.6168,-484.7027,214.0806,375.6649,444.5303,-71.8577,-474.5957,-342.2716,-322.7281,205.6087,-14.3469,-283.0586,-86.2198,-420.3924,182.3599,22.7485,452.8141,-286.5839,155.1115,-316.4854,-28.3824,56.4873,-146.001,378.2396,473.2566,380.2417,-399.6208,-347.9016,206.5985,-145.9688,-219.9708,-216.6865,404.4334,324.8516,55.3154,-119.4645,-79.2847,-191.5158,-136.3728,413.3355,356.7344,-437.7335,404.9099,-494.6143,135.9107,151.2158,-161.0672,451.0975,-93.0876,495.7659,321.2577,-451.6211,-311.9214,-432.4626,496.8637,382.6126,97.7431,245.2208,-462.5156,-274.939,116.6882,80.6219,315.5602,-342.4345,274.387,-418.7591,53.5711,-96.2339,271.8546,-46.8098,150.3864,206.6682,311.9593,174.7625,-198.5948,105.6143,212.7571,237.4211,-21.2842,-383.0439,285.4973,-80.4955,105.5129,-158.8626,-156.2353,98.5192,-308.2654,-92.7883,45.686,-380.6921,140.1508,365.9526,108.1565,-140.4508,-246.5095,133.3693,-4.6582,-20.843,339.374,-99.2908,17.8824,242.8291,75.8953,-441.8762,-352.3943,-484.0549,-401.3674,321.6953,213.7102,261.1824,-41.5899,65.2736,-26.9977,152.9615,308.5357,-211.4979,477.2073,-414.7828,-330.2034,-123.7898,-261.1105,-328.6632,-15.1514,438.4531,-323.3771,-173.6672,-293.5578,459.1075,-18.34,-270.1311,-315.6445,348.4226,-435.2806,-419.9553,-106.1863,-283.0003,43.5508,-18.0891,224.808,406.4155,-163.6988,-129.2904,207.8322,474.5666,-60.1079,9.563,44.705,118.7999,-301.6795,-38.2161,410.4003,-190.4926,-430.6086,1.2693,312.7535,-455.5725,-271.7346,-159.4378,-227.9918,312.9331,166.2825,-31.7905,-227.9038,-421.644,296.5264,-335.4129,413.344,48.8782,217.3682,434.8719,-387.0484,170.5191,201.0157,127.1522,474.5561,-100.6847,-434.2549,29.5853,-467.6037,184.2936,116.9028,124.6507,-497.3002,-86.4991,59.6243,-104.9888,-294.6228,223.8354,-97.9298,64.2283,203.7397,186.3586,64.5045,122.1795,439.3753,464.9225,434.9882,85.5836,259.4985,70.5414,-117.1196,198.2037,-127.745,-200.2022,-386.0653,1.6688,272.3237,211.4442,445.0575,479.2069,-354.0842,-211.1788,160.3409,258.6131,-71.1154,-196.203,-95.1323,-398.3867,70.6868,15.5394,333.5079,187.8193,-393.7479,269.1152,-336.0885,339.4546,-147.6351,186.847,-126.4872,-108.1731,-70.3962,-389.0454,135.3408,-51.5671,4.6139,-3.1587,-274.941,-208.586,171.0845,-277.1015,-104.1653,-260.934,-310.5456,290.0738,-38.1867,-254.3353,31.6405,433.6526,86.9343,48.5563,137.4622,-34.6388,-1.5028,-452.3147,349.1007,-347.9019,70.4255,-201.5194,-430.2517,177.8199,-391.6226,20.1876,-287.8148,-190.1158,-356.0897,-319.7011,87.2696,-141.1962,-137.9268,-70.4841,95.4435,16.2261,191.5316,-214.8942,142.0224,209.0575,180.5105,26.1511,-497.0902,-186.2708,441.5505,-7.6379,23.9577,-401.2169,-339.3474,16.9572,269.8157,178.6692,299.5455,-367.3993,-413.7073,-96.9188,-472.0939,-327.975,129.6294,446.5669,-32.714,-120.6079,71.7334,190.4871,436.6714,110.0289,-108.4299,8.0033,-341.055,77.7304,-196.1335,-343.1391,-152.6897,-378.0097,-106.9584,395.4607,-98.6717,-131.0531,-140.8907,-185.3101,-68.8474,-478.2088,-18.3317,256.0313,-119.4212,334.7436,318.1335,-20.8287,-147.7622,118.1926,-218.2094,-478.7367,217.0914,219.1878,75.2151,231.5097,-410.8572,-46.2061,153.4654,264.0178,144.8928,-115.1857,-369.8591,126.6643,-122.1998,480.7727,-85.4362,134.3245,-34.403,124.6945,12.1795,-184.8116,390.6826,87.9712,367.0822,-233.2724,-245.9838,104.6339,-53.7753,-264.3381,50.9031,-122.0604,136.6276,465.3429,288.8934,5.7445,-325.7759,53.493,-441.8264,-271.3847,-371.3886,-272.7637,-102.4757,-358.4499,-143.2793,-64.6363,499.8284,-155.8017,-37.8801,63.5318,-377.6101,125.3457,57.231,49.3608,-245.5766,-47.9802,383.4127,-114.1047,-30.258,-479.6988,-194.4846,368.4079,466.1545,-26.7084,8.2433,74.9479,-155.4871,494.9634,-196.3082,-206.8022,423.2288,-494.5835,-291.7666,-204.8478,396.6,-418.9048,-130.0584,-137.5258,-440.7922,73.1423,-251.5694,356.1615,-34.088,-23.3318,43.2522,-297.3896,409.686,-305.5675,424.8321,-154.9096,181.7696,-87.5939,-151.7475,-319.3074,227.2369,-113.0086,-68.1299,368.0398,-20.3706,-296.0095,-269.9336,-250.5127,-56.5895,188.9818,82.7481,488.6398,-151.2088,11.8563,320.4209,316.3155,317.2716,-185.4569,128.2219,108.4381,-453.2648,-406.1359,-414.2863,36.6919,-160.1338,188.7767,364.4688,-13.3882,233.621,11.2764,-154.8894,424.1841,-128.4954,23.1408,183.1928,382.2918,-464.2506,234.1366,-447.21,-425.1161,66.1712,424.058,299.3596,372.7703,-162.3764,-37.8575,-468.5142,189.9036,172.0345,310.1368,-459.7659,-219.5317,-68.9306,211.4315,-408.8232,215.1716,-134.0617,367.326,385.2393,453.6431,-258.6041,194.9712,-266.8576,145.4018,-406.4884,119.3747,466.6835,-404.694,-480.8574,-3.1007,-48.0469,-70.915,-229.4956,-69.6999,-114.9404,372.8744,-247.5689,250.4333,252.9375,71.5672,323.3984,268.7582,16.7518,-258.5373,252.518,378.1721,-197.3271,-211.1179,444.2923,-152.2646,262.3183,159.3338 + -457.1953,259.6788,271.9127,-26.8833,403.0915,-56.9197,-445.8869,-108.8167,417.8988,13.4232,-281.765,-405.8573,262.7831,-279.493,328.5591,-453.3941,-116.0368,435.4734,-439.0927,-332.9565,355.4955,324.9878,33.3519,-165.0182,188.1811,467.3455,185.1057,-233.8598,-17.6827,283.4271,-329.1247,-402.9721,404.7866,-358.7031,-267.4074,441.8363,320.2389,-128.0179,339.544,196.2018,-60.2688,336.0228,-440.1943,318.6882,-158.2596,277.0925,-487.4971,-338.9865,-275.716,136.8547,-253.6206,-40.2807,-357.0971,188.0344,-203.0674,449.9618,-223.2508,468.1441,302.4002,-65.0044,342.4431,205.6774,-118.636,-29.9706,183.9825,223.956,314.0691,137.0129,-8.0452,-15.131,-269.8643,-12.691,228.9777,-147.8384,-347.1117,-283.1905,459.2004,296.1321,-483.1799,414.3423,383.0187,-408.5525,-286.8169,482.5853,9.5232,-459.4968,-333.2521,109.0969,129.5107,43.4369,455.8283,-4.0423,-318.5019,339.1641,416.3581,-309.0429,84.2325,-355.8753,264.7671,43.8922,-298.6039,412.4413,19.4198,-251.279,-191.157,-478.2058,251.5709,-178.9633,479.293,188.399,380.9755,268.6575,120.3467,-322.0305,-255.4894,-377.515,56.9153,-133.9486,156.2546,-428.9581,-54.994,28.2146,158.7121,-426.7307,491.0086,-150.7205,-233.1005,244.5174,45.911,-406.1181,233.1636,175.9334,414.2805,421.7396,-322.8029,-252.2412,35.7622,318.5223,-141.5121,-375.4407,380.3081,222.1228,443.7844,367.377,-202.9594,-493.6231,-184.2242,-253.9838,463.1952,-416.3887,252.0867,-63.5317,411.0727,98.6261,330.7369,363.5685,-498.1848,-413.7246,-2.5996,-238.3547,-355.6041,-303.698,43.6266,383.1105,-72.3066,274.7491,321.9322,220.9543,-30.5578,400.0891,-181.7069,-386.4403,497.2206,-408.9611,138.485,-133.5666,-340.2569,-223.6313,270.884,-215.9399,74.3931,-244.1364,353.4219,-156.9905,488.3148,96.352,401.8525,-468.8344,129.9715,-27.1953,-168.631,187.7049,-336.5255,331.0652,204.3538,36.0182,366.8502,-468.6579,478.1409,-332.6136,-281.8499,63.7165,-458.8161,14.8894,-145.6397,267.1499,85.2025,326.3764,-419.6361,-133.9626,102.0618,443.3099,-207.9032,132.7032,234.001,-26.0754,105.6478,174.1252,-403.3511,-164.9714,-262.9344,-58.9668,357.6414,355.7508,-331.8443,153.5733,417.5712,260.7394,-150.1053,-435.6525,-364.1558,328.6183,-270.0863,107.1746,345.7998,480.8749,206.3896,-498.237,495.0835,481.9384,418.5571,-246.5213,-363.7304,311.7076,-53.1664,-297.3839,122.3105,-13.9226,-145.9754,-189.1748,460.9375,194.5417,-28.1346,-261.2177,-88.8396,-254.6407,-465.3148,-169.5377,24.3113,-116.2323,-420.3526,317.2107,-231.6227,-270.8239,387.8598,412.4251,428.1373,308.2044,275.2082,402.3663,-209.9843,-492.7269,225.1948,326.469,207.3557,-131.7677,371.9408,-139.3098,324.205,-126.6204,-335.0853,-248.2587,-344.907,307.2109,-441.3296,-318.027,414.6535,172.0537,-280.4991,331.0475,-158.0178,-285.1951,12.3632,149.9347,282.8302,-91.5624,-180.6097,496.0881,368.2567,357.6875,-194.2106,48.9213,-479.2956,-165.139,238.7811,302.7007,297.2805,208.7099,-5.5755,-85.7911,-358.1111,344.6131,415.7199,-219.1525,490.5003,-46.0096,498.2818,-91.8067,384.0104,396.1107,408.2827,-5.3919,-333.7992,-168.985,273.72,359.7125,227.7621,158.3406,-366.9722,3.7709,27.2728,71.9754,269.5792,-365.281,117.9152,-184.3682,356.9013,-142.6579,-496.7598,122.0194,89.1247,4.1914,-81.9905,465.0841,115.4727,169.6116,-199.9951,-223.3149,-447.3022,11.831,320.2368,105.1316,344.2462,8.6333,62.2285,-70.3944,-284.6694,-482.4229,-448.1569,-237.7858,222.3921,-172.1386,-312.5756,-390.0565,398.951,119.9784,-419.6537,121.3186,481.3011,-181.6662,-56.0219,424.1359,7.1461,138.8567,-307.0606,334.066,254.0897,473.7227,45.5936,133.7268,49.5334,-283.3406,179.4466,105.6191,-30.4162,271.5774,6.1156,110.4732,286.4325,13.3431,494.0139,-371.7624,283.3652,272.0558,-302.343,122.7245,-463.9261,299.9807,282.4502,-262.4911,183.4289,222.7474,-229.5973,141.6188,262.5468,278.1155,-331.0891,-393.6027,-230.1461,201.6657,-93.3604,-395.8877,-125.2013,-222.973,368.3759,234.6628,-28.6809,-151.0703,432.0315,253.1214,430.7065,-143.6963,499.84,85.1683,280.4354,196.6013,139.0476,120.8148,-398.8155,-335.5504,229.0516,403.8604,-383.9868,-79.975,-152.77,220.4036,135.0355,238.2176,-242.3085,-177.0743,381.8202,411.167,378.0153,456.5976,364.013,24.2316,-395.4659,-210.2581,138.7539,479.7398,-291.7797,-123.0491,188.9817,42.8931,-354.4479,358.853,-43.6168,-190.6656,-103.3037,47.8915,-358.5402,374.9758,493.9951,-427.2376,-119.1142,-453.2975,-326.2696,-212.8273,-142.2931,-179.795,355.77,-156.2903,331.2006,451.9252,185.2944,-96.1941,173.0447,345.2744,43.0151,381.7845,-143.4125,84.654,-208.7053,-293.141,333.6349,-80.472,-376.9817,214.6298,-43.0931,-254.7834,-421.6961,-368.844,467.5544,-418.61,-66.6824,-350.2671,348.8241,252.3495,41.8677,-128.869,90.0391,-136.7405,-136.7822,489.8074,-396.8204,63.8355,323.9557,-83.6674,451.263,152.8955,-291.7497,410.0787,-299.7468,51.34,-298.6066,-58.853,325.911,-281.9541,-15.3457,299.1325,-347.4959,388.407,343.1096,28.1816,24.3013,-111.3312,190.5583,279.9848,-479.8894,123.2182,233.8425,-466.2128,-134.7122,217.8674,432.9523,-186.799,-477.2512,-223.5514,64.274,141.5251,-161.2187,150.2791,-228.1087,81.172,451.0879,-230.3818,-304.9398,402.1081,199.1266,275.3423,-123.9548,-21.1815,-384.544,446.9626,208.9692,-337.4827,-58.1011,344.2642,230.2868,44.9176,245.9885,-284.1875,-351.6104,108.1289,459.649,191.4334,53.591,136.7139,10.5912,-15.8411,62.8305,448.5256,194.7705,-356.3214,84.4996,-133.2502,-358.6308,262.7949,219.8741,-355.3985,468.2922,243.7227,-408.3166,188.6111,-221.7264,-286.8234,-340.3046,-224.5375,332.2615,73.2788,-24.7857,-485.2204,-136.7196,-162.9693,92.6017,-99.611,-186.5203,495.5483,240.8051,409.6493,-58.1321,-154.1239,-335.9719,-82.4408,-471.3057,-43.373,301.0884,-96.6359,-236.6906,435.7313,-227.7263,-406.8904,-392.3187,169.0043,-371.0852,-271.3652,-57.4466,-196.8455,52.741,361.7395,-117.8599,190.5339,276.6457,-321.9851,425.881,-473.2662,-74.2968,221.3612,-465.4429,181.723,-78.4508,21.6152,148.8107,-166.1687,-281.6391,-462.3636,-420.5255,-161.4143,98.8383,-374.5345,-366.2851,187.1506,-405.1865,239.4847,-246.8352,33.1748,-344.1211,477.9759,-294.1354,-359.5015,-44.8454,151.7072,-22.7324,-260.3293,99.1414,-20.5536,173.3766,-422.6692,458.3853,-199.7898,-236.3929,365.2599,-66.4191,388.3472,283.0336,-268.9463,269.5704,360.9679,-322.102,-407.0705,-93.0994,338.9108,-189.1359,-216.9102,-249.0153,122.6058,-254.8318,-112.2771,-279.0506,-168.4431,392.888,394.7607,468.0544,340.1852,-293.1288,-8.2912,-419.2608,323.3382,-93.8793,-242.0672,427.7716,-441.6906,128.3229,424.4679,-71.8586,134.5411,-74.5205,18.4141,17.7277,126.9123,-137.6119,33.3783,222.9912,-279.3582,89.1226,-90.031,12.7221,98.7767,-80.2372,-485.9212,-481.6575,-325.9729,318.8005,-433.786,-296.6337,421.6515,-27.2786,-445.2456,451.8876,-482.1014,-143.1098,186.1258,-90.2432,-297.7479,-351.0026,-423.7518,-219.6096,-269.2043,33.5767,-325.4335,392.4866,-418.243,112.5852,-248.1306,451.2154,-419.2995,154.5752,483.6323,-315.962,-196.872,406.1769,-356.9868,67.5251,-255.6475,103.5181,-450.4418,386.9518,456.4057,99.4591,-166.636,275.5374,200.4925,99.7623,292.6794,-422.3998,419.4837,-466.548,-462.8519,-381.4489,472.8356,-129.9563,441.4941,-376.1232,-114.1945,233.5531,313.6963,394.9503,-278.7558,350.7515,47.9427,220.7074,-178.9789,-346.0485,-128.5665,8.9461,159.9838,-57.3637,351.9478,-65.9411,-258.1788,498.9494,-472.613,-428.5678,17.3981,-435.3682,-421.155,-54.9177,-490.2348,178.3777,-31.9618,-242.1805,362.3736,380.8179,446.4272,-23.9142,61.3588,-489.5704,363.6446,-186.1519,-351.8684,-322.2791,-226.0431,404.6996,203.9824,306.0958,234.0145,-180.4996,452.0633,257.171,-83.6197,-393.152,396.6934,32.156,-428.7645,183.7886,494.767,68.3905,278.9785,-40.4759,261.7298,236.5778,4.5577,-130.9582,433.2837,-298.1139,-107.9822,-196.8446,-121.1765,-292.5509,-246.4546,-258.6038,280.1334,-52.6511,483.2928,-185.7577,-75.3705,351.3411,179.1282,-479.3838,166.2733,-197.9043,282.6848,-50.4744,-492.7178,183.6435,-127.2379,483.646,433.0805,-228.5488,139.8314,-145.1337,-403.1749,306.2704,122.7149,479.6928,85.3866,108.095,-224.152,494.6848,-368.4504,-180.7579,61.7136,51.2045,-383.0103,-376.4816,-292.8217,-201.118,332.1516,425.2758,138.1284,-229.4302,432.9081,2.9898,-437.7631,-448.2151,129.9126,-170.2405,499.0396,-48.2137,363.8046,-423.2511,-28.0804,-267.826,-356.6288,-99.9371,-409.8465,170.4902,-269.2584,-277.4098,300.8819,-142.5889,339.0952,16.2275,-310.8646,201.0733,-495.5905,341.9279,-149.1184,-494.4928,-81.7343,209.9762,273.4892,380.3163,359.2424,-242.5,-42.1268,-303.9792,11.6018,361.5483,416.4178,10.3282,195.9796,148.8096,-60.9724,-205.5221,-145.4574,-341.5913,426.8996,-19.5843,60.6265,-133.4191,-139.8737,281.7465,461.2854,-270.8902,61.0182,-58.6791,-254.0193,-234.1206,-208.7334,39.7498,-14.337,-68.2319,-342.2756,403.6834,401.6122,-166.1637,47.3592,-325.7,274.5459,343.4873,328.3783,-370.1657,-122.8967,-231.3182,122.6609,119.2685,-223.5437,-210.8076,116.5022,340.2814,256.1852,-217.3487,-150.9598,331.1343,-453.8182,-448.0842,-95.2475,-340.9942,-416.7835,-96.7226,-328.7212,-373.4337,472.2214,-484.522,-465.1583,330.0712,73.2052,-55.1266,-352.8984,341.0742,-230.4845,321.0752,236.2116,35.1902,75.3489,-469.4042,110.2036,35.1156,454.7224,103.0685,-221.7499,-23.6898,-259.2362,-110.509,-261.0039,219.2391,-139.9404,155.7723,377.9713,434.0318,-365.1397,459.1471,-318.5774,323.4256,194.325,-311.9529,-153.9019,-346.5811,76.4069,443.2121,-199.407,495.6636,-138.5213,-145.3432,-151.7758,-365.3547,263.6507,-491.1686,-183.5585,-12.6044,318.5346,-443.8639,-179.0338,477.9093,-355.5118,-423.0035,-229.1166,-96.7782,-479.2384,192.9085,223.3407,-302.9472,297.3847,477.584,-297.5958,168.6023,-80.6912,-89.8717,87.1476,-129.7807,346.5576,-253.9729,-399.6858,-389.5785,35.1648,-180.451,-49.6084,83.9582,-185.2329,97.283,195.5249,-91.6969,199.202,-449.792,333.4825,-113.7558,443.434,394.3587,-94.9074,71.2092,-251.1774,-85.047,-46.4004,20.2595,341.1073,-91.2527,86.3775,303.1247,-336.9011,343.9894,-384.1261,154.4411,-465.2493,-63.3249,488.0231,348.6725,458.2093,322.401,220.2532,283.3734,-386.4252,-256.5262,-87.2205,96.8199,47.6908,-399.6307,214.7716,-19.9177,-458.513,-194.3218,-320.5342,-275.857,-301.6955,-84.9038,358.3475,-88.9271,499.7721,-161.7403,355.4894,313.6211,-176.1703,61.8427,107.603,-176.063,-426.5408,292.3612,58.3331,-115.8853,471.4131,-76.4815,-309.6263,361.4518,192.4763,-145.7968,256.3888,133.335,-474.0901,-366.9793,-495.223,457.2366,170.056,285.0152,89.8213,225.2251,354.1822,-298.374,-332.9164,-55.2409,306.9283,25.9392,218.0624,7.5085,-151.8768,-155.4932,6.0001,201.4506,-259.9874,485.1078,-362.8516,-230.1434,-398.2512,243.0012,32.302,-197.91,144.1195,-89.4196,-44.0399,-371.7866,227.6007,492.7526,499.3824,162.2475,279.0325,177.0781,341.0137,199.6009,108.1678,312.2319,-211.5001,-92.675,357.0513,-337.924,-348.984,-350.3677,173.3473,-193.7346,-318.5609,-2.0928,46.6287,-346.8513,36.634,-277.4949,-149.325,481.1378,370.3864,-139.6689,-332.2805,48.0292,109.8363,494.6994,373.6992,495.7442,400.4998,-26.2276,-308.7669,188.9497,257.9182,-116.6944,269.8932,197.005,123.1139,-356.2058,485.1982,-4.0119,397.8434,-204.67,-494.5133,-414.1299,142.1512,-36.5446,390.0718,6.9876,263.1216,457.5598,89.6086,-266.3804,17.3457,88.8182,236.6271,81.175,-170.2249,-5.7664,422.7852,180.3349,-135.2642,149.2285,-70.6607,-46.169,-389.3313,230.6125,388.4853,-438.3426,111.8034,300.0416,37.5604,-437.3868,-114.1336,312.7777,-99.1161,-312.9015,-147.3787,-434.0536,19.5034,141.706,-281.4504,-208.9608,281.4619,-361.0596,-464.2757,77.8205,232.5575,165.4104,424.8738,124.5555,342.038,86.7543,278.0216,311.2686,337.834,-90.0545,-210.1143,-488.4095,-80.7535,92.3731,-122.622,-288.0571,1.7285,-5.2998,100.0717,-395.0571,-477.5587,-160.5642,-119.4214,-232.233,415.7276,-204.3216,-436.7766,-103.4644,-427.0939,-31.0927,-440.2919,120.5971,-223.3623,-199.0988,304.8697,432.5731,-231.5791,-397.696,306.4134,330.1018,32.4345,-175.719,464.6091,-291.5686,300.1631,-167.4592,238.9574,104.5893,-187.2215,-294.0111,-361.9094,480.6847,-304.2133,-448.7144,67.7235,-255.9669,254.7379,464.5465,6.8909,-368.7554,337.5993,39.1928,-376.0625,433.4224,-109.1488,341.7731,377.843,446.839,-192.283,251.1592,437.6812,-478.3409,345.7668,377.965,125.6188,-462.0904,-235.3324,316.8892,-460.7371,248.9306,418.7082,-333.7257,-104.5062,-408.1356,148.6624,-158.4929,-477.0664,80.4926,-214.6292,211.3377,322.7854,-312.851,403.0215,-213.3089,-71.3355,-276.1068,-293.0902,-277.4559,54.2176,-119.1285,-479.4361,-492.6072,8.3732,42.4988,-5.576,-198.6151,-357.0952,-331.5667,186.6195,317.3075,201.267,-37.1731,-278.3164,-467.7796,-163.3909,-117.305,-233.9266,277.7969,181.9723,178.8292,-168.7152,-436.041,171.345,369.0302,423.7144,434.0961,-428.1816,23.7334,-136.6735,-222.4486,180.8461,57.5968,129.2984,127.1866,-109.3928,-143.6253,-385.9948,127.9867,-8.8096,-239.844,66.6491,-50.7301,-309.1113,-474.6991,212.1767,-444.4596,-211.3601,351.3551,335.0507,-128.6226,-98.5249,-257.454,489.8014,-378.8622,311.0304,-4.9107,362.7586,-458.8825,373.2779,-103.29,-5.6216,122.0183,76.9731,17.8771,289.8893,-56.4338,375.9665,-83.9991,440.0823,142.2309,-471.0813,-59.4847,-400.4217,91.4892,374.4009,486.8697,414.5213,-0.3535,-278.2345,-231.206,-238.479,389.3143,-276.9742,-33.9869,349.1201,127.3928,-410.7213,337.3789,36.4048,333.4291,-12.4075,483.8778,311.4489,-74.0628,-379.6051,463.234,157.5614,-140.9455,120.7926,-161.2341,194.162,-412.6181,-9.1258,-194.5065,441.1572,255.5455,-73.8086,-119.4013,-486.4792,-27.4352,98.9738,-119.002,-75.5589,261.7675,156.0993,89.6457,-190.6318,429.9325,195.9536,-172.6155,-22.7976,438.9412,-246.4661,447.7281,434.5346,405.8957,217.3324,392.6129,-158.604,15.8632,483.0414,334.7693,-307.2482,302.1267,-7.4125,3.8081,-405.7316,377.5069,51.2307,235.0695,269.737,-389.3487,186.4225,-36.8521,401.2051,-59.0378,-190.8023,-182.8076,-362.6136,-124.8064,362.4142,45.3344,-330.1214,-162.5452,-434.4411,219.1143,-374.1038,364.5639,-268.582,-22.9247,-73.8849,-54.5258,-23.0882,167.9233,-181.9807,-207.1173,300.2193,206.5903,-72.013,-244.4396,-435.5389,10.3523,-435.3545,-138.8392,449.8426,-244.8971,229.7666,267.5225,-401.6021,466.3278,418.3623,-317.8205,28.5192,384.5628,-79.6177,469.4532,-395.1986,-353.4477,-93.6914,70.3999,-441.0627,-201.1221,141.2748,433.3389,82.413,-394.0046,-438.6836,453.4704,-160.6535,353.0374,-238.0377,236.5195,497.9019,202.9472,-421.6417,-382.042,84.6308,430.1599,-390.9918,-195.0401,255.6526,-86.5964,-491.667,-199.1557,-102.7114,474.877,-292.9154,-77.3163,143.5625,58.8126,-284.8908,-457.6457,212.5317,480.4032,-324.0829,491.0165,-494.7934,267.4311,-142.2401,-368.9058,-370.4955,498.803,-6.7377,-395.373,177.8868,306.9761,80.4185,-239.1253,-435.1349,7.6298,-157.6242,348.6095,475.7845,317.7116,-353.7336,-40.2881,353.7096,-60.9783,-385.5816,243.8071,-398.8341,62.343,340.0251,-24.8105,-343.4186,189.6737,-467.3026,104.7127,159.5467,-482.5496,71.6951,-163.5304,-321.8438,185.2875,-331.6885,-102.6817,-242.7548,-259.4407,220.6898,231.6571,-297.1145,-186.9472,-316.9286,-36.2392,-293.964,296.3878,467.7409,-277.6389,493.2143,417.1244,12.241,-343.7893,-33.7207,457.2978,-248.9726,-409.5439,-92.4779,-173.7584,400.8483,59.7439,13.3265,-175.617,37.333,-307.6469,-82.3687,332.578,-412.0079,144.7037,350.6506,423.3235,-53.2147,67.9581,-447.3845,-461.0187,371.1702,386.2045,352.2722,-119.098,123.9178,-52.0535,465.2626,474.0272,402.9961,491.4763,-33.1373,-228.8607,-383.3299,408.8192,-275.155,489.8633,-349.5073,346.9781,129.3929,282.1868,-77.3384,277.3026,412.3277,263.6705,473.3756,-437.9988,114.1686,-452.3331,-167.8898,-193.6217,444.6168,-354.3223,-238.0967,432.0883,-349.7249,-42.3659,-304.7343,296.2192,-136.5386,-121.7774,450.4678,140.5384,-450.8993,93.8942,-54.4945,498.521,-461.7182,111.5166,-397.6007,-397.959,-20.9331,-19.7068,78.551,161.9472,-24.8682,-434.4537,102.9447,214.298,-494.3813,211.6782,64.8196,372.6962,-399.8337,114.5476,-191.0045,-369.6465,-391.7201,-204.9951,-201.7654,475.898,-262.3247,-348.6974,79.4062,-112.4281,-102.266,67.3008,335.485,68.4289,-433.9104,-392.963,-73.3788,276.5766,-105.2219,422.6201,192.915,-388.3541,242.3915,479.5633,42.5998,259.6189,-316.5861,390.1121,-216.0274,-373.296,103.7169,321.9107,19.0023,487.2627,151.6922,276.7424,461.6928,24.4758,133.263,-47.289,-413.9538,435.2414,-466.9724,-270.6602,238.9442,-110.5389,403.5151,-395.4393,-208.2219,-53.0773,-26.5792,-387.6534,-120.5566,143.2237,-305.3778,442.0665,417.9523,460.3337,254.8689,-375.9436,-101.0153,232.4727,-35.5285,-470.3007,-423.9161,-108.9997,-29.6555,233.1043,240.4766,404.763,276.8465,-354.4058,74.0678,-343.244,332.9786,361.2964,-322.0828,-41.1861,-122.8074,-299.5682,-481.218,-157.3994,310.6317,-261.176,310.2644,-239.9855,255.1004,-311.3351,437.9486,78.1311,-133.9261,-176.2119,45.9943,492.3169,266.5795,16.8553,-470.9413,-331.2718,218.4122,369.7118,-179.3201,-165.7277,-87.9832,357.6499,-261.0345,442.1609,113.2997,-112.5643,481.2426,-365.4958,400.5374,-395.085,303.8103,-292.0268,167.0744,-199.013,174.9283,498.3585,-337.466,303.9078,-326.0901,-331.7143,6.7189,-277.1371,-204.9097,-313.4259,-462.7296,437.8485,267.2872,157.752,143.8784,60.1304,-492.991,326.0132,-123.3415,390.8461,-293.0175,483.4759,240.4338,271.6879,483.4801,391.2687,238.3995,-246.607,-411.7722,-257.9864,238.0949,494.3455,-489.0838,-26.7283,317.1161,-264.0242,-16.6819,-141.4839,429.101,252.2336,-325.1541,471.044,452.352,7.4546,343.3004,-336.4424,489.6317,307.1831,-139.2075,153.572,-332.5617,-361.892,110.6459,-384.8117,-423.0834,-277.9929,44.5303,167.9458,364.1204,-222.5008,-148.7923,198.4694,-74.0043,-458.4327,-227.5346,272.4441,-477.2587,303.1998,72.3129,112.9422,-98.2577,296.903,-489.0569,-461.4503,-381.6239,-440.6212,-354.1834,356.1583,-220.6533,192.5295,-409.0818,-264.2973,498.2192,-306.675,-313.6103,-124.9266,-436.5922,297.9051,121.9351,425.3888,-283.9925,-360.441,-347.4517,8.6814,477.4163,-344.6926,-311.574,-199.9541,-272.862,-360.8642,-306.0856,-218.9529,200.1938,-187.9337,-149.341,-431.5156,-135.3958,131.1299,262.0532,-210.162,353.4392,-249.2969,216.4223,499.6139,215.8176,-346.1569,177.2202,-173.1132,-466.9007,-310.9848,463.485,6.516,-334.8823,-282.7409,-375.2367,-127.4937,257.2427,384.9285,206.4053,-283.9167,369.6312,-325.1146,452.7523,-103.9792,-51.036,153.325,-344.1749,289.4824,109.8308,375.2284,-249.8481,367.8478,71.0143,471.6136,-265.6336,12.9061,-470.1288,-113.547,38.8925,-205.7232,418.6063,475.6095,-18.8731,-431.5545,-288.6452,-406.8928,79.4828,-152.1474,345.565,-200.8038,174.7789,379.2991,-385.1188,-217.6888,241.9077,-449.1824,467.832,186.0095,-82.8376,-450.7827,-32.2903,-288.132,169.8581,-275.3198,-388.1222,-431.3601,64.9652,368.9351,107.4999,408.8666,267.7858,-462.4349,-198.4615,378.1182,252.7529,-344.883,-364.0161,-124.6144,-222.8902,-103.7114,387.1701,-363.7944,-237.934,230.2082,-63.1276,-456.8188,361.9248,461.0643,160.8127,305.6079,81.2236,-322.0002,-273.4727,-356.9758,227.4751,278.5386,-10.8627,49.6988,-495.2527,428.0901,393.6169,-360.5547,-137.0244,26.962,-326.3379,-399.4972,449.7645,-238.7444,-69.8461,222.6126,-68.7657,132.7567,255.7355,-190.3762,271.6129,405.5764,115.8834,0.9645,331.1665,396.4585,217.4435,-323.6914,39.5915,282.4489,411.3888,-219.2131,240.8913,-109.5264,-438.3067,-157.3961,-180.7485,-258.9153,61.7008,483.4718,-386.0406,-499.1824,-90.2675,-358.5152,-79.3051,-97.4094,-91.7246,63.539,-307.0526,226.416,-454.475,-375.7449,300.532,409.7526,7.7042,-320.297,-244.9896,-282.6645,-414.9866,-331.4623,316.162,348.8361,-342.8609,477.2374,6.5636,-483.931,341.3556,498.2318,-46.3428,203.981,101.2793,128.4547,-285.068,56.5149,-407.6478,-151.4672,116.6673,-115.0498,-491.7974,-151.9475,474.7827,-288.4179,286.4447,-430.6331,-279.1458,318.721,-276.8375,157.9586,-9.2346,398.8374,380.2256,61.1557,13.0746,-80.139,-134.8798,-37.6466,-209.7381,236.1511,388.5629,-196.1123,-481.5887,327.8334,408.2074,479.1439,85.082,227.7623,250.2644,-47.8238,464.8471,-431.5099,489.9794,452.9999,-50.8695,-429.0862,-138.8555,-395.3346,391.3405,-249.4682,-280.6761,-460.5297,1.0129,199.1008,-97.4134,-235.0172,-466.1287,-302.7993,298.4108,-22.478,173.9936,122.8033,-235.0353,231.5057,-97.2265,-203.8224,457.6806,484.1385,-309.3619,-168.3588,-177.2797,-3.9408,-279.2997,104.4862,-139.4921,-450.2539,402.541,-437.1151,-337.4914,-200.3446,-164.484,-293.7216,471.7414,192.6153,233.1926,-122.8377,356.5476,450.1361,-400.0941,61.0466,441.7145,189.7192,-69.6348,252.5418,-246.5242,-344.0219,14.2904,87.2185,-119.2684,205.422,-374.4802,33.4042,81.2271,-2.5025,-138.6816,8.1989,-439.7698,-446.1887,-374.9012,160.9795,49.3705,72.7925,245.9454,-138.7558,11.9923,414.9421,5.9535,-142.9589,396.2571,-222.2068,-2.6172,-90.5871,346.7415,-337.3213,-372.4473,91.8271,310.6442,263.7468,-357.0433,-246.0827,25.4967,55.8069,-64.7183,-342.7375,-356.7083,70.0885,-79.026,-346.3906,206.2687,-440.6602,321.8775,223.3025,159.6939,292.4308,241.077,-219.0901,495.9946,0.3506,-166.4262,475.1836,-272.5527,118.8711,458.2456,353.3839,-82.5653,37.2834,-92.4387,146.5082,233.4743,-408.0537,-469.9263,148.8959,-324.352,498.608,-324.5319,-114.6779,-200.4192,404.8448,-289.7989,400.6151,-372.9065,359.7581,141.4237,-304.6837,314.3738,-302.4693,442.6138,-224.0818,270.1887,-477.1098,429.0239,264.1871,26.84,283.4518,129.5215,6.6673,-91.4464,75.821,261.5692,-403.0782,-213.9284,-356.8221,-232.4484,33.5696,99.1931,344.0097,187.4695,-264.0572,-199.6103,342.5485,187.058,31.5948,-275.4046,215.9846,425.1114,327.1992,437.8426,-281.2049,71.7953,393.346,-339.9023,-78.8502,314.1866,-120.7207,-416.0802,-327.1001,413.6143,-236.2051,247.1197,318.5011,-194.295,486.3421,409.0831,252.6212,-452.654,-215.7497,-464.1643,61.9033,66.4139,-425.8918,-401.3522,-395.1639,427.7052,-264.1728,131.9144,258.4416,-442.2357,68.3167,441.5518,138.4774,470.7538,-14.6434,-436.2225,385.0708,286.1155,323.9014,137.4596,-352.5503,1.9307,-314.7656,449.5639,-468.3008,81.2499,487.4562,270.1387,-445.3627,460.1174,-205.2539,-32.6044,359.0438,-115.5841,-268.6624,-495.8554,-474.4781,337.9834,-281.4488,252.1636,-33.645,-26.6636,193.8834,287.2377,6.9748,414.4343,-211.7143,-23.0035,-226.5275,-400.285,-336.3935,28.1908,244.27,21.9938,-222.3759,-103.1418,464.7943,-256.0156,46.7511,-487.2509,-321.3631,479.2142,328.166,-481.2039,253.4962,100.2875,-399.98,-81.5868,289.7597,-318.7266,-264.2078,129.4063,407.6828,222.8346,370.0391,46.9838,-356.4992,-305.9992,-258.4048,-410.7736,-245.9092,32.9185,-237.9085,-403.8853,12.0239,-164.6252,107.369,8.0379,-139.3796,365.9266,-448.5863,314.1141,-280.0686,-463.4747,2.6092,-376.8811,96.7462,242.419,-480.9968,345.3697,328.281,39.0387,-342.3026,469.0461,-103.9411,381.0458,-141.6771,-4.7988,289.4799,-55.0671,-292.4788,364.1267,-395.9876,-232.5859,-285.7012,-444.7762,79.5454,251.5539,359.3705,467.2154,273.1778,-373.8216,299.611,-464.32,-106.0638,491.2626,-39.3721,-110.1154,383.4063,45.0848,262.2361,-111.754,249.0826,-305.9751,22.9663,-120.4794,484.0797,151.9063,388.5088,105.9067,444.0361,-45.5696,243.9313,303.4003,-27.795,-7.2151,411.6561,-100.6193,-207.3277,-6.4576,-300.3722,118.2638,342.3654,66.7861,104.0615,180.5752,281.6788,-342.7549,-65.8778,140.9091,-169.8935,-437.2435,-392.4147,-348.2217,202.3684,440.4071,-276.2247,129.5096,-43.4059,-456.876,-445.1126,-193.8847,-156.3408,274.7116,-129.6168,-484.7027,214.0806,375.6649,444.5303,-71.8577,-474.5957,-342.2716,-322.7281,205.6087,-14.3469,-283.0586,-86.2198,-420.3924,182.3599,22.7485,452.8141,-286.5839,155.1115,-316.4854,-28.3824,56.4873,-146.001,378.2396,473.2566,380.2417,-399.6208,-347.9016,206.5985,-145.9688,-219.9708,-216.6865,404.4334,324.8516,55.3154,-119.4645,-79.2847,-191.5158,-136.3728,413.3355,356.7344,-437.7335,404.9099,-494.6143,135.9107,151.2158,-161.0672,451.0975,-93.0876,495.7659,321.2577,-451.6211,-311.9214,-432.4626,496.8637,382.6126,97.7431,245.2208,-462.5156,-274.939,116.6882,80.6219,315.5602,-342.4345,274.387,-418.7591,53.5711,-96.2339,271.8546,-46.8098,150.3864,206.6682,311.9593,174.7625,-198.5948,105.6143,212.7571,237.4211,-21.2842,-383.0439,285.4973,-80.4955,105.5129,-158.8626,-156.2353,98.5192,-308.2654,-92.7883,45.686,-380.6921,140.1508,365.9526,108.1565,-140.4508,-246.5095,133.3693,-4.6582,-20.843,339.374,-99.2908,17.8824,242.8291,75.8953,-441.8762,-352.3943,-484.0549,-401.3674,321.6953,213.7102,261.1824,-41.5899,65.2736,-26.9977,152.9615,308.5357,-211.4979,477.2073,-414.7828,-330.2034,-123.7898,-261.1105,-328.6632,-15.1514,438.4531,-323.3771,-173.6672,-293.5578,459.1075,-18.34,-270.1311,-315.6445,348.4226,-435.2806,-419.9553,-106.1863,-283.0003,43.5508,-18.0891,224.808,406.4155,-163.6988,-129.2904,207.8322,474.5666,-60.1079,9.563,44.705,118.7999,-301.6795,-38.2161,410.4003,-190.4926,-430.6086,1.2693,312.7535,-455.5725,-271.7346,-159.4378,-227.9918,312.9331,166.2825,-31.7905,-227.9038,-421.644,296.5264,-335.4129,413.344,48.8782,217.3682,434.8719,-387.0484,170.5191,201.0157,127.1522,474.5561,-100.6847,-434.2549,29.5853,-467.6037,184.2936,116.9028,124.6507,-497.3002,-86.4991,59.6243,-104.9888,-294.6228,223.8354,-97.9298,64.2283,203.7397,186.3586,64.5045,122.1795,439.3753,464.9225,434.9882,85.5836,259.4985,70.5414,-117.1196,198.2037,-127.745,-200.2022,-386.0653,1.6688,272.3237,211.4442,445.0575,479.2069,-354.0842,-211.1788,160.3409,258.6131,-71.1154,-196.203,-95.1323,-398.3867,70.6868,15.5394,333.5079,187.8193,-393.7479,269.1152,-336.0885,339.4546,-147.6351,186.847,-126.4872,-108.1731,-70.3962,-389.0454,135.3408,-51.5671,4.6139,-3.1587,-274.941,-208.586,171.0845,-277.1015,-104.1653,-260.934,-310.5456,290.0738,-38.1867,-254.3353,31.6405,433.6526,86.9343,48.5563,137.4622,-34.6388,-1.5028,-452.3147,349.1007,-347.9019,70.4255,-201.5194,-430.2517,177.8199,-391.6226,20.1876,-287.8148,-190.1158,-356.0897,-319.7011,87.2696,-141.1962,-137.9268,-70.4841,95.4435,16.2261,191.5316,-214.8942,142.0224,209.0575,180.5105,26.1511,-497.0902,-186.2708,441.5505,-7.6379,23.9577,-401.2169,-339.3474,16.9572,269.8157,178.6692,299.5455,-367.3993,-413.7073,-96.9188,-472.0939,-327.975,129.6294,446.5669,-32.714,-120.6079,71.7334,190.4871,436.6714,110.0289,-108.4299,8.0033,-341.055,77.7304,-196.1335,-343.1391,-152.6897,-378.0097,-106.9584,395.4607,-98.6717,-131.0531,-140.8907,-185.3101,-68.8474,-478.2088,-18.3317,256.0313,-119.4212,334.7436,318.1335,-20.8287,-147.7622,118.1926,-218.2094,-478.7367,217.0914,219.1878,75.2151,231.5097,-410.8572,-46.2061,153.4654,264.0178,144.8928,-115.1857,-369.8591,126.6643,-122.1998,480.7727,-85.4362,134.3245,-34.403,124.6945,12.1795,-184.8116,390.6826,87.9712,367.0822,-233.2724,-245.9838,104.6339,-53.7753,-264.3381,50.9031,-122.0604,136.6276,465.3429,288.8934,5.7445,-325.7759,53.493,-441.8264,-271.3847,-371.3886,-272.7637,-102.4757,-358.4499,-143.2793,-64.6363,499.8284,-155.8017,-37.8801,63.5318,-377.6101,125.3457,57.231,49.3608,-245.5766,-47.9802,383.4127,-114.1047,-30.258,-479.6988,-194.4846,368.4079,466.1545,-26.7084,8.2433,74.9479,-155.4871,494.9634,-196.3082,-206.8022,423.2288,-494.5835,-291.7666,-204.8478,396.6,-418.9048,-130.0584,-137.5258,-440.7922,73.1423,-251.5694,356.1615,-34.088,-23.3318,43.2522,-297.3896,409.686,-305.5675,424.8321,-154.9096,181.7696,-87.5939,-151.7475,-319.3074,227.2369,-113.0086,-68.1299,368.0398,-20.3706,-296.0095,-269.9336,-250.5127,-56.5895,188.9818,82.7481,488.6398,-151.2088,11.8563,320.4209,316.3155,317.2716,-185.4569,128.2219,108.4381,-453.2648,-406.1359,-414.2863,36.6919,-160.1338,188.7767,364.4688,-13.3882,233.621,11.2764,-154.8894,424.1841,-128.4954,23.1408,183.1928,382.2918,-464.2506,234.1366,-447.21,-425.1161,66.1712,424.058,299.3596,372.7703,-162.3764,-37.8575,-468.5142,189.9036,172.0345,310.1368,-459.7659,-219.5317,-68.9306,211.4315,-408.8232,215.1716,-134.0617,367.326,385.2393,453.6431,-258.6041,194.9712,-266.8576,145.4018,-406.4884,119.3747,466.6835,-404.694,-480.8574,-3.1007,-48.0469,-70.915,-229.4956,-69.6999,-114.9404,372.8744,-247.5689,250.4333,252.9375,71.5672,323.3984,268.7582,16.7518,-258.5373,252.518,378.1721,-197.3271,-211.1179,444.2923,-152.2646,262.3183,159.3338,259.6788,271.9127,-26.8833,403.0915,-56.9197,-445.8869,-108.8167,417.8988,13.4232,-281.765,-405.8573,262.7831,-279.493,328.5591,-453.3941,-116.0368,435.4734,-439.0927,-332.9565,355.4955,324.9878,33.3519,-165.0182,188.1811,467.3455,185.1057,-233.8598,-17.6827,283.4271,-329.1247,-402.9721,404.7866,-358.7031,-267.4074,441.8363,320.2389,-128.0179,339.544,196.2018,-60.2688,336.0228,-440.1943,318.6882,-158.2596,277.0925,-487.4971,-338.9865,-275.716,136.8547,-253.6206,-40.2807,-357.0971,188.0344,-203.0674,449.9618,-223.2508,468.1441,302.4002,-65.0044,342.4431,205.6774,-118.636,-29.9706,183.9825,223.956,314.0691,137.0129,-8.0452,-15.131,-269.8643,-12.691,228.9777,-147.8384,-347.1117,-283.1905,459.2004,296.1321,-483.1799,414.3423,383.0187,-408.5525,-286.8169,482.5853,9.5232,-459.4968,-333.2521,109.0969,129.5107,43.4369,455.8283,-4.0423,-318.5019,339.1641,416.3581,-309.0429,84.2325,-355.8753,264.7671,43.8922,-298.6039,412.4413,19.4198,-251.279,-191.157,-478.2058,251.5709,-178.9633,479.293,188.399,380.9755,268.6575,120.3467,-322.0305,-255.4894,-377.515,56.9153,-133.9486,156.2546,-428.9581,-54.994,28.2146,158.7121,-426.7307,491.0086,-150.7205,-233.1005,244.5174,45.911,-406.1181,233.1636,175.9334,414.2805,421.7396,-322.8029,-252.2412,35.7622,318.5223,-141.5121,-375.4407,380.3081,222.1228,443.7844,367.377,-202.9594,-493.6231,-184.2242,-253.9838,463.1952,-416.3887,252.0867,-63.5317,411.0727,98.6261,330.7369,363.5685,-498.1848,-413.7246,-2.5996,-238.3547,-355.6041,-303.698,43.6266,383.1105,-72.3066,274.7491,321.9322,220.9543,-30.5578,400.0891,-181.7069,-386.4403,497.2206,-408.9611,138.485,-133.5666,-340.2569,-223.6313,270.884,-215.9399,74.3931,-244.1364,353.4219,-156.9905,488.3148,96.352,401.8525,-468.8344,129.9715,-27.1953,-168.631,187.7049,-336.5255,331.0652,204.3538,36.0182,366.8502,-468.6579,478.1409,-332.6136,-281.8499,63.7165,-458.8161,14.8894,-145.6397,267.1499,85.2025,326.3764,-419.6361,-133.9626,102.0618,443.3099,-207.9032,132.7032,234.001,-26.0754,105.6478,174.1252,-403.3511,-164.9714,-262.9344,-58.9668,357.6414,355.7508,-331.8443,153.5733,417.5712,260.7394,-150.1053,-435.6525,-364.1558,328.6183,-270.0863,107.1746,345.7998,480.8749,206.3896,-498.237,495.0835,481.9384,418.5571,-246.5213,-363.7304,311.7076,-53.1664,-297.3839,122.3105,-13.9226,-145.9754,-189.1748,460.9375,194.5417,-28.1346,-261.2177,-88.8396,-254.6407,-465.3148,-169.5377,24.3113,-116.2323,-420.3526,317.2107,-231.6227,-270.8239,387.8598,412.4251,428.1373,308.2044,275.2082,402.3663,-209.9843,-492.7269,225.1948,326.469,207.3557,-131.7677,371.9408,-139.3098,324.205,-126.6204,-335.0853,-248.2587,-344.907,307.2109,-441.3296,-318.027,414.6535,172.0537,-280.4991,331.0475,-158.0178,-285.1951,12.3632,149.9347,282.8302,-91.5624,-180.6097,496.0881,368.2567,357.6875,-194.2106,48.9213,-479.2956,-165.139,238.7811,302.7007,297.2805,208.7099,-5.5755,-85.7911,-358.1111,344.6131,415.7199,-219.1525,490.5003,-46.0096,498.2818,-91.8067,384.0104,396.1107,408.2827,-5.3919,-333.7992,-168.985,273.72,359.7125,227.7621,158.3406,-366.9722,3.7709,27.2728,71.9754,269.5792,-365.281,117.9152,-184.3682,356.9013,-142.6579,-496.7598,122.0194,89.1247,4.1914,-81.9905,465.0841,115.4727,169.6116,-199.9951,-223.3149,-447.3022,11.831,320.2368,105.1316,344.2462,8.6333,62.2285,-70.3944,-284.6694,-482.4229,-448.1569,-237.7858,222.3921,-172.1386,-312.5756,-390.0565,398.951,119.9784,-419.6537,121.3186,481.3011,-181.6662,-56.0219,424.1359,7.1461,138.8567,-307.0606,334.066,254.0897,473.7227,45.5936,133.7268,49.5334,-283.3406,179.4466,105.6191,-30.4162,271.5774,6.1156,110.4732,286.4325,13.3431,494.0139,-371.7624,283.3652,272.0558,-302.343,122.7245,-463.9261,299.9807,282.4502,-262.4911,183.4289,222.7474,-229.5973,141.6188,262.5468,278.1155,-331.0891,-393.6027,-230.1461,201.6657,-93.3604,-395.8877,-125.2013,-222.973,368.3759,234.6628,-28.6809,-151.0703,432.0315,253.1214,430.7065,-143.6963,499.84,85.1683,280.4354,196.6013,139.0476,120.8148,-398.8155,-335.5504,229.0516,403.8604,-383.9868,-79.975,-152.77,220.4036,135.0355,238.2176,-242.3085,-177.0743,381.8202,411.167,378.0153,456.5976,364.013,24.2316,-395.4659,-210.2581,138.7539,479.7398,-291.7797,-123.0491,188.9817,42.8931,-354.4479,358.853,-43.6168,-190.6656,-103.3037,47.8915,-358.5402,374.9758,493.9951,-427.2376,-119.1142,-453.2975,-326.2696,-212.8273,-142.2931,-179.795,355.77,-156.2903,331.2006,451.9252,185.2944,-96.1941,173.0447,345.2744,43.0151,381.7845,-143.4125,84.654,-208.7053,-293.141,333.6349,-80.472,-376.9817,214.6298,-43.0931,-254.7834,-421.6961,-368.844,467.5544,-418.61,-66.6824,-350.2671,348.8241,252.3495,41.8677,-128.869,90.0391,-136.7405,-136.7822,489.8074,-396.8204,63.8355,323.9557,-83.6674,451.263,152.8955,-291.7497,410.0787,-299.7468,51.34,-298.6066,-58.853,325.911,-281.9541,-15.3457,299.1325,-347.4959,388.407,343.1096,28.1816,24.3013,-111.3312,190.5583,279.9848,-479.8894,123.2182,233.8425,-466.2128,-134.7122,217.8674,432.9523,-186.799,-477.2512,-223.5514,64.274,141.5251,-161.2187,150.2791,-228.1087,81.172,451.0879,-230.3818,-304.9398,402.1081,199.1266,275.3423,-123.9548,-21.1815,-384.544,446.9626,208.9692,-337.4827,-58.1011,344.2642,230.2868,44.9176,245.9885,-284.1875,-351.6104,108.1289,459.649,191.4334,53.591,136.7139,10.5912,-15.8411,62.8305,448.5256,194.7705,-356.3214,84.4996,-133.2502,-358.6308,262.7949,219.8741,-355.3985,468.2922,243.7227,-408.3166,188.6111,-221.7264,-286.8234,-340.3046,-224.5375,332.2615,73.2788,-24.7857,-485.2204,-136.7196,-162.9693,92.6017,-99.611,-186.5203,495.5483,240.8051,409.6493,-58.1321,-154.1239,-335.9719,-82.4408,-471.3057,-43.373,301.0884,-96.6359,-236.6906,435.7313,-227.7263,-406.8904,-392.3187,169.0043,-371.0852,-271.3652,-57.4466,-196.8455,52.741,361.7395,-117.8599,190.5339,276.6457,-321.9851,425.881,-473.2662,-74.2968,221.3612,-465.4429,181.723,-78.4508,21.6152,148.8107,-166.1687,-281.6391,-462.3636,-420.5255,-161.4143,98.8383,-374.5345,-366.2851,187.1506,-405.1865,239.4847,-246.8352,33.1748,-344.1211,477.9759,-294.1354,-359.5015,-44.8454,151.7072,-22.7324,-260.3293,99.1414,-20.5536,173.3766,-422.6692,458.3853,-199.7898,-236.3929,365.2599,-66.4191,388.3472,283.0336,-268.9463,269.5704,360.9679,-322.102,-407.0705,-93.0994,338.9108,-189.1359,-216.9102,-249.0153,122.6058,-254.8318,-112.2771,-279.0506,-168.4431,392.888,394.7607,468.0544,340.1852,-293.1288,-8.2912,-419.2608,323.3382,-93.8793,-242.0672,427.7716,-441.6906,128.3229,424.4679,-71.8586,134.5411,-74.5205,18.4141,17.7277,126.9123,-137.6119,33.3783,222.9912,-279.3582,89.1226,-90.031,12.7221,98.7767,-80.2372,-485.9212,-481.6575,-325.9729,318.8005,-433.786,-296.6337,421.6515,-27.2786,-445.2456,451.8876,-482.1014,-143.1098,186.1258,-90.2432,-297.7479,-351.0026,-423.7518,-219.6096,-269.2043,33.5767,-325.4335,392.4866,-418.243,112.5852,-248.1306,451.2154,-419.2995,154.5752,483.6323,-315.962,-196.872,406.1769,-356.9868,67.5251,-255.6475,103.5181,-450.4418,386.9518,456.4057,99.4591,-166.636,275.5374,200.4925,99.7623,292.6794,-422.3998,419.4837,-466.548,-462.8519,-381.4489,472.8356,-129.9563,441.4941,-376.1232,-114.1945,233.5531,313.6963,394.9503,-278.7558,350.7515,47.9427,220.7074,-178.9789,-346.0485,-128.5665,8.9461,159.9838,-57.3637,351.9478,-65.9411,-258.1788,498.9494,-472.613,-428.5678,17.3981,-435.3682,-421.155,-54.9177,-490.2348,178.3777,-31.9618,-242.1805,362.3736,380.8179,446.4272,-23.9142,61.3588,-489.5704,363.6446,-186.1519,-351.8684,-322.2791,-226.0431,404.6996,203.9824,306.0958,234.0145,-180.4996,452.0633,257.171,-83.6197,-393.152,396.6934,32.156,-428.7645,183.7886,494.767,68.3905,278.9785,-40.4759,261.7298,236.5778,4.5577,-130.9582,433.2837,-298.1139,-107.9822,-196.8446,-121.1765,-292.5509,-246.4546,-258.6038,280.1334,-52.6511,483.2928,-185.7577,-75.3705,351.3411,179.1282,-479.3838,166.2733,-197.9043,282.6848,-50.4744,-492.7178,183.6435,-127.2379,483.646,433.0805,-228.5488,139.8314,-145.1337,-403.1749,306.2704,122.7149,479.6928,85.3866,108.095,-224.152,494.6848,-368.4504,-180.7579,61.7136,51.2045,-383.0103,-376.4816,-292.8217,-201.118,332.1516,425.2758,138.1284,-229.4302,432.9081,2.9898,-437.7631,-448.2151,129.9126,-170.2405,499.0396,-48.2137,363.8046,-423.2511,-28.0804,-267.826,-356.6288,-99.9371,-409.8465,170.4902,-269.2584,-277.4098,300.8819,-142.5889,339.0952,16.2275,-310.8646,201.0733,-495.5905,341.9279,-149.1184,-494.4928,-81.7343,209.9762,273.4892,380.3163,359.2424,-242.5,-42.1268,-303.9792,11.6018,361.5483,416.4178,10.3282,195.9796,148.8096,-60.9724,-205.5221,-145.4574,-341.5913,426.8996,-19.5843,60.6265,-133.4191,-139.8737,281.7465,461.2854,-270.8902,61.0182,-58.6791,-254.0193,-234.1206,-208.7334,39.7498,-14.337,-68.2319,-342.2756,403.6834,401.6122,-166.1637,47.3592,-325.7,274.5459,343.4873,328.3783,-370.1657,-122.8967,-231.3182,122.6609,119.2685,-223.5437,-210.8076,116.5022,340.2814,256.1852,-217.3487,-150.9598,331.1343,-453.8182,-448.0842,-95.2475,-340.9942,-416.7835,-96.7226,-328.7212,-373.4337,472.2214,-484.522,-465.1583,330.0712,73.2052,-55.1266,-352.8984,341.0742,-230.4845,321.0752,236.2116,35.1902,75.3489,-469.4042,110.2036,35.1156,454.7224,103.0685,-221.7499,-23.6898,-259.2362,-110.509,-261.0039,219.2391,-139.9404,155.7723,377.9713,434.0318,-365.1397,459.1471,-318.5774,323.4256,194.325,-311.9529,-153.9019,-346.5811,76.4069,443.2121,-199.407,495.6636,-138.5213,-145.3432,-151.7758,-365.3547,263.6507,-491.1686,-183.5585,-12.6044,318.5346,-443.8639,-179.0338,477.9093,-355.5118,-423.0035,-229.1166,-96.7782,-479.2384,192.9085,223.3407,-302.9472,297.3847,477.584,-297.5958,168.6023,-80.6912,-89.8717,87.1476,-129.7807,346.5576,-253.9729,-399.6858,-389.5785,35.1648,-180.451,-49.6084,83.9582,-185.2329,97.283,195.5249,-91.6969,199.202,-449.792,333.4825,-113.7558,443.434,394.3587,-94.9074,71.2092,-251.1774,-85.047,-46.4004,20.2595,341.1073,-91.2527,86.3775,303.1247,-336.9011,343.9894,-384.1261,154.4411,-465.2493,-63.3249,488.0231,348.6725,458.2093,322.401,220.2532,283.3734,-386.4252,-256.5262,-87.2205,96.8199,47.6908,-399.6307,214.7716,-19.9177,-458.513,-194.3218,-320.5342,-275.857,-301.6955,-84.9038,358.3475,-88.9271,499.7721,-161.7403,355.4894,313.6211,-176.1703,61.8427,107.603,-176.063,-426.5408,292.3612,58.3331,-115.8853,471.4131,-76.4815,-309.6263,361.4518,192.4763,-145.7968,256.3888,133.335,-474.0901,-366.9793,-495.223,457.2366,170.056,285.0152,89.8213,225.2251,354.1822,-298.374,-332.9164,-55.2409,306.9283,25.9392,218.0624,7.5085,-151.8768,-155.4932,6.0001,201.4506,-259.9874,485.1078,-362.8516,-230.1434,-398.2512,243.0012,32.302,-197.91,144.1195,-89.4196,-44.0399,-371.7866,227.6007,492.7526,499.3824,162.2475,279.0325,177.0781,341.0137,199.6009,108.1678,312.2319,-211.5001,-92.675,357.0513,-337.924,-348.984,-350.3677,173.3473,-193.7346,-318.5609,-2.0928,46.6287,-346.8513,36.634,-277.4949,-149.325,481.1378,370.3864,-139.6689,-332.2805,48.0292,109.8363,494.6994,373.6992,495.7442,400.4998,-26.2276,-308.7669,188.9497,257.9182,-116.6944,269.8932,197.005,123.1139,-356.2058,485.1982,-4.0119,397.8434,-204.67,-494.5133,-414.1299,142.1512,-36.5446,390.0718,6.9876,263.1216,457.5598,89.6086,-266.3804,17.3457,88.8182,236.6271,81.175,-170.2249,-5.7664,422.7852,180.3349,-135.2642,149.2285,-70.6607,-46.169,-389.3313,230.6125,388.4853,-438.3426,111.8034,300.0416,37.5604,-437.3868,-114.1336,312.7777,-99.1161,-312.9015,-147.3787,-434.0536,19.5034,141.706,-281.4504,-208.9608,281.4619,-361.0596,-464.2757,77.8205,232.5575,165.4104,424.8738,124.5555,342.038,86.7543,278.0216,311.2686,337.834,-90.0545,-210.1143,-488.4095,-80.7535,92.3731,-122.622,-288.0571,1.7285,-5.2998,100.0717,-395.0571,-477.5587,-160.5642,-119.4214,-232.233,415.7276,-204.3216,-436.7766,-103.4644,-427.0939,-31.0927,-440.2919,120.5971,-223.3623,-199.0988,304.8697,432.5731,-231.5791,-397.696,306.4134,330.1018,32.4345,-175.719,464.6091,-291.5686,300.1631,-167.4592,238.9574,104.5893,-187.2215,-294.0111,-361.9094,480.6847,-304.2133,-448.7144,67.7235,-255.9669,254.7379,464.5465,6.8909,-368.7554,337.5993,39.1928,-376.0625,433.4224,-109.1488,341.7731,377.843,446.839,-192.283,251.1592,437.6812,-478.3409 ] - do: indices.get_mapping: @@ -189,7 +189,7 @@ setup: match: "*dense_vector*" mapping: type: dense_vector - dims: 3000 + dims: 5000 index: true similarity: cosine diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java index 365d4f615e30c..91f2165f6b0d1 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java @@ -74,7 +74,7 @@ public class DenseVectorFieldMapper extends FieldMapper { public static final IndexVersion LITTLE_ENDIAN_FLOAT_STORED_INDEX_VERSION = IndexVersion.V_8_9_0; public static final String CONTENT_TYPE = "dense_vector"; - public static short MAX_DIMS_COUNT = 2048; // maximum allowed number of dimensions + public static short MAX_DIMS_COUNT = 4096; // maximum allowed number of dimensions public static short MIN_DIMS_FOR_DYNAMIC_FLOAT_MAPPING = 128; // minimum number of dims for floats to be dynamically mapped to vector public static final int MAGNITUDE_BYTES = 4; diff --git a/server/src/main/java/org/elasticsearch/script/field/vectors/DenseVector.java b/server/src/main/java/org/elasticsearch/script/field/vectors/DenseVector.java index 79a4c3fa1b2ee..d18ae16746819 100644 --- a/server/src/main/java/org/elasticsearch/script/field/vectors/DenseVector.java +++ b/server/src/main/java/org/elasticsearch/script/field/vectors/DenseVector.java @@ -19,7 +19,7 @@ * 1) float[], this is for the ScoreScriptUtils class bindings which have converted a List based query vector into an array * 2) List, A painless script will typically use Lists since they are easy to pass as params and have an easy * literal syntax. Working with Lists directly, instead of converting to a float[], trades off runtime operations against - * memory pressure. Dense Vectors may have high dimensionality, up to 2048. Allocating a float[] per doc per script API + * memory pressure. Dense Vectors may have high dimensionality, up to 4096. Allocating a float[] per doc per script API * call is prohibitively expensive. * 3) Object, the whitelisted method for the painless API. Calls into the float[] or List version based on the class of the argument and checks dimensionality. diff --git a/server/src/test/java/org/elasticsearch/action/search/KnnSearchSingleNodeTests.java b/server/src/test/java/org/elasticsearch/action/search/KnnSearchSingleNodeTests.java index d859575a2cdaf..3794fb0d85c8a 100644 --- a/server/src/test/java/org/elasticsearch/action/search/KnnSearchSingleNodeTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/KnnSearchSingleNodeTests.java @@ -409,7 +409,7 @@ public void testKnnSearchAction() throws IOException { assertEquals(2, response.getHits().getHits().length); } - public void testKnnVectorsWith2048Dims() throws IOException { + public void testKnnVectorsWith4096Dims() throws IOException { int numShards = 1 + randomInt(3); Settings indexSettings = Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numShards).build(); @@ -418,7 +418,7 @@ public void testKnnVectorsWith2048Dims() throws IOException { .startObject("properties") .startObject("vector") .field("type", "dense_vector") - .field("dims", 2048) + .field("dims", 4096) .field("index", true) .field("similarity", "l2_norm") .endObject() @@ -427,18 +427,18 @@ public void testKnnVectorsWith2048Dims() throws IOException { createIndex("index", indexSettings, builder); for (int doc = 0; doc < 10; doc++) { - client().prepareIndex("index").setSource("vector", randomVector(2048)).get(); + client().prepareIndex("index").setSource("vector", randomVector(4096)).get(); } indicesAdmin().prepareRefresh("index").get(); - float[] queryVector = randomVector(2048); + float[] queryVector = randomVector(4096); KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector", queryVector, 3, 50, null).boost(5.0f); SearchResponse response = client().prepareSearch("index").setKnnSearch(List.of(knnSearch)).addFetchField("*").setSize(10).get(); assertHitCount(response, 3); assertEquals(3, response.getHits().getHits().length); - assertEquals(2048, response.getHits().getAt(0).field("vector").getValues().size()); + assertEquals(4096, response.getHits().getAt(0).field("vector").getValues().size()); } private float[] randomVector() { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java index c2762d859f266..5b911ee1348db 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java @@ -197,7 +197,7 @@ public void testDims() { assertThat( e.getMessage(), equalTo( - "Failed to parse mapping: " + "The number of dimensions for field [field] should be in the range [1, 2048] but was [0]" + "Failed to parse mapping: " + "The number of dimensions for field [field] should be in the range [1, 4096] but was [0]" ) ); } @@ -205,13 +205,13 @@ public void testDims() { { Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(fieldMapping(b -> { b.field("type", "dense_vector"); - b.field("dims", 3000); + b.field("dims", 5000); }))); assertThat( e.getMessage(), equalTo( "Failed to parse mapping: " - + "The number of dimensions for field [field] should be in the range [1, 2048] but was [3000]" + + "The number of dimensions for field [field] should be in the range [1, 4096] but was [5000]" ) ); } @@ -220,13 +220,13 @@ public void testDims() { Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(fieldMapping(b -> { b.field("type", "dense_vector"); b.field("index", "true"); - b.field("dims", 3000); + b.field("dims", 5000); }))); assertThat( e.getMessage(), equalTo( "Failed to parse mapping: " - + "The number of dimensions for field [field] should be in the range [1, 2048] but was [3000]" + + "The number of dimensions for field [field] should be in the range [1, 4096] but was [5000]" ) ); } @@ -597,10 +597,10 @@ public void testDocumentsWithIncorrectDims() throws Exception { /** * Test that max dimensions limit for float dense_vector field - * is 2048 as defined by {@link DenseVectorFieldMapper#MAX_DIMS_COUNT} + * is 4096 as defined by {@link DenseVectorFieldMapper#MAX_DIMS_COUNT} */ public void testMaxDimsFloatVector() throws IOException { - final int dims = 2048; + final int dims = 4096; VectorSimilarity similarity = VectorSimilarity.COSINE; DocumentMapper mapper = createDocumentMapper( fieldMapping(b -> b.field("type", "dense_vector").field("dims", dims).field("index", true).field("similarity", similarity)) @@ -624,10 +624,10 @@ public void testMaxDimsFloatVector() throws IOException { /** * Test that max dimensions limit for byte dense_vector field - * is 2048 as defined by {@link KnnByteVectorField} + * is 4096 as defined by {@link KnnByteVectorField} */ public void testMaxDimsByteVector() throws IOException { - final int dims = 2048; + final int dims = 4096; VectorSimilarity similarity = VectorSimilarity.COSINE; ; DocumentMapper mapper = createDocumentMapper( @@ -703,7 +703,7 @@ protected void assertFetch(MapperService mapperService, String field, Object val @Override // TODO: add `byte` element_type tests protected void randomFetchTestFieldConfig(XContentBuilder b) throws IOException { - b.field("type", "dense_vector").field("dims", randomIntBetween(2, 2048)).field("element_type", "float"); + b.field("type", "dense_vector").field("dims", randomIntBetween(2, 4096)).field("element_type", "float"); if (randomBoolean()) { b.field("index", true).field("similarity", randomFrom(VectorSimilarity.values()).toString()); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldTypeTests.java index d22056d49beb5..1f9013502144e 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldTypeTests.java @@ -158,39 +158,39 @@ public void testFloatCreateKnnQuery() { } public void testCreateKnnQueryMaxDims() { - { // float type with 2048 dims - DenseVectorFieldType fieldWith2048dims = new DenseVectorFieldType( + { // float type with 4096 dims + DenseVectorFieldType fieldWith4096dims = new DenseVectorFieldType( "f", IndexVersion.current(), DenseVectorFieldMapper.ElementType.FLOAT, - 2048, + 4096, true, VectorSimilarity.COSINE, Collections.emptyMap() ); - float[] queryVector = new float[2048]; - for (int i = 0; i < 2048; i++) { + float[] queryVector = new float[4096]; + for (int i = 0; i < 4096; i++) { queryVector[i] = randomFloat(); } - Query query = fieldWith2048dims.createKnnQuery(queryVector, 10, null, null); + Query query = fieldWith4096dims.createKnnQuery(queryVector, 10, null, null); assertThat(query, instanceOf(KnnFloatVectorQuery.class)); } - { // byte type with 2048 dims - DenseVectorFieldType fieldWith2048dims = new DenseVectorFieldType( + { // byte type with 4096 dims + DenseVectorFieldType fieldWith4096dims = new DenseVectorFieldType( "f", IndexVersion.current(), DenseVectorFieldMapper.ElementType.BYTE, - 2048, + 4096, true, VectorSimilarity.COSINE, Collections.emptyMap() ); - byte[] queryVector = new byte[2048]; - for (int i = 0; i < 2048; i++) { + byte[] queryVector = new byte[4096]; + for (int i = 0; i < 4096; i++) { queryVector[i] = randomByte(); } - Query query = fieldWith2048dims.createKnnQuery(queryVector, 10, null, null); + Query query = fieldWith4096dims.createKnnQuery(queryVector, 10, null, null); assertThat(query, instanceOf(KnnByteVectorQuery.class)); } } From e38d0a1b5d4b8894c55ba49dbf3eb8d86bd83398 Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Wed, 20 Sep 2023 16:10:04 -0500 Subject: [PATCH 003/155] Treating watcher webhook response header names as case-insensitive (#99717) --- docs/changelog/99717.yaml | 5 +++++ .../xpack/watcher/common/http/HttpClient.java | 14 ++++++++++---- .../xpack/watcher/common/http/HttpClientTests.java | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 docs/changelog/99717.yaml diff --git a/docs/changelog/99717.yaml b/docs/changelog/99717.yaml new file mode 100644 index 0000000000000..db48c69ed68a2 --- /dev/null +++ b/docs/changelog/99717.yaml @@ -0,0 +1,5 @@ +pr: 99717 +summary: Treating watcher webhook response header names as case-insensitive +area: Watcher +type: bug +issues: [] diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java index 59aac833111c0..e70e1ba349086 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java @@ -72,6 +72,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -274,17 +275,22 @@ public HttpResponse execute(HttpRequest request) throws IOException { // headers Header[] headers = response.getAllHeaders(); Map responseHeaders = Maps.newMapWithExpectedSize(headers.length); + /* + * Headers are not case sensitive, so in the following loop we lowercase all of them. We also roll up all values for the same + * case-insensitive header into a list. + */ for (Header header : headers) { - if (responseHeaders.containsKey(header.getName())) { - String[] old = responseHeaders.get(header.getName()); + String lowerCaseHeaderName = header.getName().toLowerCase(Locale.ROOT); + if (responseHeaders.containsKey(lowerCaseHeaderName)) { + String[] old = responseHeaders.get(lowerCaseHeaderName); String[] values = new String[old.length + 1]; System.arraycopy(old, 0, values, 0, old.length); values[values.length - 1] = header.getValue(); - responseHeaders.put(header.getName(), values); + responseHeaders.put(lowerCaseHeaderName, values); } else { - responseHeaders.put(header.getName(), new String[] { header.getValue() }); + responseHeaders.put(lowerCaseHeaderName, new String[] { header.getValue() }); } } diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java index 2a6138380afff..0aac3cb4463e4 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpClientTests.java @@ -500,6 +500,7 @@ public void testThatDuplicateHeaderKeysAreReturned() throws Exception { .setBody("foo") .addHeader("foo", "bar") .addHeader("foo", "baz") + .addHeader("Foo", "bam") .addHeader("Content-Length", "3"); webServer.enqueue(mockResponse); @@ -509,7 +510,7 @@ public void testThatDuplicateHeaderKeysAreReturned() throws Exception { assertThat(webServer.requests(), hasSize(1)); assertThat(httpResponse.headers(), hasKey("foo")); - assertThat(httpResponse.headers().get("foo"), containsInAnyOrder("bar", "baz")); + assertThat(httpResponse.headers().get("foo"), containsInAnyOrder("bar", "baz", "bam")); } // finally fixing https://github.com/elastic/x-plugins/issues/1141 - yay! Fixed due to switching to apache http client internally! From 50abfd3508861850b292bad6394ef3c8c3083bcb Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Thu, 21 Sep 2023 08:07:52 +0200 Subject: [PATCH 004/155] Add flamegraph API (#99091) With this commit we add a new API call `/_profiling/flamegraph` that builds on top of the existing `/_profiling/stacktraces` API but moves all computation of a flamegraph from Kibana to Elasticsearch. --- docs/changelog/99091.yaml | 5 + .../xcontent/ChunkedToXContentHelper.java | 4 + .../elasticsearch/xpack/profiling/Frame.java | 10 + .../xpack/profiling/GetFlamegraphAction.java | 18 ++ .../profiling/GetFlamegraphResponse.java | 153 ++++++++++ .../profiling/GetStackTracesRequest.java | 15 + .../profiling/GetStackTracesResponse.java | 5 + .../xpack/profiling/ProfilingPlugin.java | 2 + .../xpack/profiling/Resampler.java | 56 ++++ .../profiling/RestGetFlamegraphAction.java | 45 +++ .../xpack/profiling/StackFrame.java | 49 ++++ .../TransportGetFlamegraphAction.java | 276 ++++++++++++++++++ .../TransportGetStackTracesAction.java | 58 +--- .../xpack/profiling/ResamplerTests.java | 108 +++++++ .../TransportGetFlamegraphActionTests.java | 61 ++++ .../xpack/security/operator/Constants.java | 1 + 16 files changed, 814 insertions(+), 52 deletions(-) create mode 100644 docs/changelog/99091.yaml create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Frame.java create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphAction.java create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Resampler.java create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/RestGetFlamegraphAction.java create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java create mode 100644 x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java create mode 100644 x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java diff --git a/docs/changelog/99091.yaml b/docs/changelog/99091.yaml new file mode 100644 index 0000000000000..2c7be19b161ba --- /dev/null +++ b/docs/changelog/99091.yaml @@ -0,0 +1,5 @@ +pr: 99091 +summary: Add flamegraph API +area: Application +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java index ce8af443a9789..692693f9e04c8 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java @@ -30,6 +30,10 @@ public static Iterator endObject() { return Iterators.single(((builder, params) -> builder.endObject())); } + public static Iterator startArray() { + return Iterators.single(((builder, params) -> builder.startArray())); + } + public static Iterator startArray(String name) { return Iterators.single(((builder, params) -> builder.startArray(name))); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Frame.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Frame.java new file mode 100644 index 0000000000000..42d830ed00477 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Frame.java @@ -0,0 +1,10 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +public record Frame(String fileName, String functionName, int functionOffset, int lineNumber, boolean inline) {} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphAction.java new file mode 100644 index 0000000000000..79f8632238d4c --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphAction.java @@ -0,0 +1,18 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.action.ActionType; + +public final class GetFlamegraphAction extends ActionType { + public static final GetFlamegraphAction INSTANCE = new GetFlamegraphAction(); + public static final String NAME = "indices:data/read/profiling/flamegraph"; + + private GetFlamegraphAction() { + super(NAME, GetFlamegraphResponse::new); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java new file mode 100644 index 0000000000000..0ab9060aa8936 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java @@ -0,0 +1,153 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.collect.Iterators; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; +import org.elasticsearch.xcontent.ToXContent; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class GetFlamegraphResponse extends ActionResponse implements ChunkedToXContentObject { + private final int size; + private final double samplingRate; + private final List> edges; + private final List fileIds; + private final List frameTypes; + private final List inlineFrames; + private final List fileNames; + private final List addressOrLines; + private final List functionNames; + private final List functionOffsets; + private final List sourceFileNames; + private final List sourceLines; + private final List countInclusive; + private final List countExclusive; + + public GetFlamegraphResponse(StreamInput in) throws IOException { + this.size = in.readInt(); + this.samplingRate = in.readDouble(); + this.edges = in.readCollectionAsList(i -> i.readMap(StreamInput::readInt)); + this.fileIds = in.readCollectionAsList(StreamInput::readString); + this.frameTypes = in.readCollectionAsList(StreamInput::readInt); + this.inlineFrames = in.readCollectionAsList(StreamInput::readBoolean); + this.fileNames = in.readCollectionAsList(StreamInput::readString); + this.addressOrLines = in.readCollectionAsList(StreamInput::readInt); + this.functionNames = in.readCollectionAsList(StreamInput::readString); + this.functionOffsets = in.readCollectionAsList(StreamInput::readInt); + this.sourceFileNames = in.readCollectionAsList(StreamInput::readString); + this.sourceLines = in.readCollectionAsList(StreamInput::readInt); + this.countInclusive = in.readCollectionAsList(StreamInput::readInt); + this.countExclusive = in.readCollectionAsList(StreamInput::readInt); + } + + public GetFlamegraphResponse( + int size, + double samplingRate, + List> edges, + List fileIds, + List frameTypes, + List inlineFrames, + List fileNames, + List addressOrLines, + List functionNames, + List functionOffsets, + List sourceFileNames, + List sourceLines, + List countInclusive, + List countExclusive + ) { + this.size = size; + this.samplingRate = samplingRate; + this.edges = edges; + this.fileIds = fileIds; + this.frameTypes = frameTypes; + this.inlineFrames = inlineFrames; + this.fileNames = fileNames; + this.addressOrLines = addressOrLines; + this.functionNames = functionNames; + this.functionOffsets = functionOffsets; + this.sourceFileNames = sourceFileNames; + this.sourceLines = sourceLines; + this.countInclusive = countInclusive; + this.countExclusive = countExclusive; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeInt(this.size); + out.writeDouble(this.samplingRate); + out.writeCollection(this.edges, (o, v) -> o.writeMap(v, StreamOutput::writeString, StreamOutput::writeInt)); + out.writeCollection(this.fileIds, StreamOutput::writeString); + out.writeCollection(this.frameTypes, StreamOutput::writeInt); + out.writeCollection(this.inlineFrames, StreamOutput::writeBoolean); + out.writeCollection(this.fileNames, StreamOutput::writeString); + out.writeCollection(this.addressOrLines, StreamOutput::writeInt); + out.writeCollection(this.functionNames, StreamOutput::writeString); + out.writeCollection(this.functionOffsets, StreamOutput::writeInt); + out.writeCollection(this.sourceFileNames, StreamOutput::writeString); + out.writeCollection(this.sourceLines, StreamOutput::writeInt); + out.writeCollection(this.countInclusive, StreamOutput::writeInt); + out.writeCollection(this.countExclusive, StreamOutput::writeInt); + } + + public int getSize() { + return size; + } + + public double getSamplingRate() { + return samplingRate; + } + + public List getCountInclusive() { + return countInclusive; + } + + public List getCountExclusive() { + return countExclusive; + } + + @Override + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + ChunkedToXContentHelper.array( + "Edges", + Iterators.flatMap( + edges.iterator(), + perNodeEdges -> Iterators.concat( + ChunkedToXContentHelper.startArray(), + Iterators.map(perNodeEdges.entrySet().iterator(), edge -> (b, p) -> b.value(edge.getValue())), + ChunkedToXContentHelper.endArray() + ) + ) + ), + ChunkedToXContentHelper.array("FileID", Iterators.map(fileIds.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("FrameType", Iterators.map(frameTypes.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("Inline", Iterators.map(inlineFrames.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("ExeFilename", Iterators.map(fileNames.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("AddressOrLine", Iterators.map(addressOrLines.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("FunctionName", Iterators.map(functionNames.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("FunctionOffset", Iterators.map(functionOffsets.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("SourceFilename", Iterators.map(sourceFileNames.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("SourceLine", Iterators.map(sourceLines.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("CountInclusive", Iterators.map(countInclusive.iterator(), e -> (b, p) -> b.value(e))), + ChunkedToXContentHelper.array("CountExclusive", Iterators.map(countExclusive.iterator(), e -> (b, p) -> b.value(e))), + Iterators.single((b, p) -> b.field("Size", size)), + Iterators.single((b, p) -> b.field("SamplingRate", samplingRate)), + ChunkedToXContentHelper.endObject() + ); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java index 244d9cd3fd830..4083776f8c4a6 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java @@ -41,6 +41,11 @@ public class GetStackTracesRequest extends ActionRequest implements IndicesReque private Integer sampleSize; + // We intentionally don't expose this field via the REST API but we can control behavior within Elasticsearch. + // Once we have migrated all client-side code to dedicated APIs (such as the flamegraph API), we can adjust + // sample counts by default and remove this flag. + private Boolean adjustSampleCount; + public GetStackTracesRequest() { this(null, null); } @@ -53,12 +58,14 @@ public GetStackTracesRequest(Integer sampleSize, QueryBuilder query) { public GetStackTracesRequest(StreamInput in) throws IOException { this.query = in.readOptionalNamedWriteable(QueryBuilder.class); this.sampleSize = in.readOptionalInt(); + this.adjustSampleCount = in.readOptionalBoolean(); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeOptionalNamedWriteable(query); out.writeOptionalInt(sampleSize); + out.writeOptionalBoolean(adjustSampleCount); } public Integer getSampleSize() { @@ -69,6 +76,14 @@ public QueryBuilder getQuery() { return query; } + public boolean isAdjustSampleCount() { + return Boolean.TRUE.equals(adjustSampleCount); + } + + public void setAdjustSampleCount(Boolean adjustSampleCount) { + this.adjustSampleCount = adjustSampleCount; + } + public void parseXContent(XContentParser parser) throws IOException { XContentParser.Token token = parser.currentToken(); String currentFieldName = null; diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java index 7a6fba1f04c84..72fed7376bde5 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java @@ -137,6 +137,10 @@ public int getTotalFrames() { return totalFrames; } + public double getSamplingRate() { + return samplingRate; + } + @Override public Iterator toXContentChunked(ToXContent.Params params) { return Iterators.concat( @@ -147,6 +151,7 @@ public Iterator toXContentChunked(ToXContent.Params params optional("stack_trace_events", stackTraceEvents, ChunkedToXContentHelper::map), Iterators.single((b, p) -> b.field("total_frames", totalFrames)), Iterators.single((b, p) -> b.field("sampling_rate", samplingRate)), + // start and end are intentionally not written to the XContent representation because we only need them on the transport layer ChunkedToXContentHelper.endObject() ); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java index 2952594122af4..037f57b36d547 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java @@ -144,6 +144,7 @@ public List getRestHandlers( handlers.add(new RestGetStatusAction()); if (enabled) { handlers.add(new RestGetStackTracesAction()); + handlers.add(new RestGetFlamegraphAction()); } return Collections.unmodifiableList(handlers); } @@ -177,6 +178,7 @@ public static ExecutorBuilder responseExecutorBuilder() { public List> getActions() { return List.of( new ActionHandler<>(GetStackTracesAction.INSTANCE, TransportGetStackTracesAction.class), + new ActionHandler<>(GetFlamegraphAction.INSTANCE, TransportGetFlamegraphAction.class), new ActionHandler<>(GetStatusAction.INSTANCE, TransportGetStatusAction.class) ); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Resampler.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Resampler.java new file mode 100644 index 0000000000000..b70807e472536 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/Resampler.java @@ -0,0 +1,56 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import java.util.Random; +import java.util.random.RandomGenerator; + +class Resampler { + private final boolean requiresResampling; + private final RandomGenerator r; + private final double adjustedSampleRate; + private final double p; + + Resampler(GetStackTracesRequest request, double sampleRate, long totalCount) { + // Manually reduce sample count if totalCount exceeds sampleSize by 10%. + if (totalCount > request.getSampleSize() * 1.1) { + this.requiresResampling = true; + // Make the RNG predictable to get reproducible results. + this.r = createRandom(request); + this.p = (double) request.getSampleSize() / totalCount; + } else { + this.requiresResampling = false; + this.r = null; + this.p = 1.0d; + } + // TODO: Just use the sample rate as is once all resampling is done server-side + this.adjustedSampleRate = request.isAdjustSampleCount() ? sampleRate : 1.0d; + } + + protected RandomGenerator createRandom(GetStackTracesRequest request) { + return new Random(request.hashCode()); + } + + public int adjustSampleCount(int originalCount) { + int rawCount; + if (requiresResampling) { + rawCount = 0; + for (int i = 0; i < originalCount; i++) { + if (r.nextDouble() < p) { + rawCount++; + } + } + } else { + rawCount = originalCount; + } + // Adjust the sample counts from down-sampled to fully sampled. + // Be aware that downsampling drops entries from stackTraceEvents, so that + // the sum of the upscaled count values is less that totalCount. + return (int) Math.floor(rawCount / (p * adjustedSampleRate)); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/RestGetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/RestGetFlamegraphAction.java new file mode 100644 index 0000000000000..a23f501de0602 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/RestGetFlamegraphAction.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.client.internal.node.NodeClient; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.action.RestActionListener; +import org.elasticsearch.rest.action.RestCancellableNodeClient; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static org.elasticsearch.rest.RestRequest.Method.POST; + +public class RestGetFlamegraphAction extends BaseRestHandler { + @Override + public List routes() { + return List.of(new Route(POST, "/_profiling/flamegraph")); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + GetStackTracesRequest getStackTracesRequest = new GetStackTracesRequest(); + request.applyContentParser(getStackTracesRequest::parseXContent); + // enforce server-side adjustment of sample counts for flamegraphs + getStackTracesRequest.setAdjustSampleCount(true); + + return channel -> { + RestActionListener listener = new RestChunkedToXContentListener<>(channel); + RestCancellableNodeClient cancelClient = new RestCancellableNodeClient(client, request.getHttpChannel()); + cancelClient.execute(GetFlamegraphAction.INSTANCE, getStackTracesRequest, listener); + }; + } + + @Override + public String getName() { + return "get_flamegraph_action"; + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/StackFrame.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/StackFrame.java index 5dc2b212ed55b..eb5134be70adb 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/StackFrame.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/StackFrame.java @@ -12,8 +12,10 @@ import java.io.IOException; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; final class StackFrame implements ToXContentObject { @@ -49,6 +51,15 @@ public static StackFrame fromSource(Map source) { ); } + public boolean isEmpty() { + return fileName.isEmpty() && functionName.isEmpty() && functionOffset.isEmpty() && lineNumber.isEmpty(); + } + + public Iterable frames() { + return new Frames(); + + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -79,4 +90,42 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(fileName, functionName, functionOffset, lineNumber); } + + private class Frames implements Iterable { + @Override + public Iterator iterator() { + return new Iterator<>() { + private int currentElement = 0; + + @Override + public boolean hasNext() { + // array lengths might not be consistent - allow to move until all underlying lists have been exhausted + return currentElement < fileName.size() + || currentElement < functionName.size() + || currentElement < functionOffset.size() + || currentElement < lineNumber.size(); + } + + @Override + public Frame next() { + if (hasNext() == false) { + throw new NoSuchElementException(); + } + Frame f = new Frame( + get(fileName, currentElement, ""), + get(functionName, currentElement, ""), + get(functionOffset, currentElement, 0), + get(lineNumber, currentElement, 0), + currentElement > 0 + ); + currentElement++; + return f; + } + }; + } + + private static T get(List l, int index, T defaultValue) { + return index < l.size() ? l.get(index) : defaultValue; + } + } } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java new file mode 100644 index 0000000000000..acef2f6661c02 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java @@ -0,0 +1,276 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; +import org.elasticsearch.client.internal.node.NodeClient; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.transport.TransportService; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +public class TransportGetFlamegraphAction extends HandledTransportAction { + private static final Logger log = LogManager.getLogger(TransportGetFlamegraphAction.class); + private static final StackFrame EMPTY_STACKFRAME = new StackFrame("", "", 0, 0); + + private final NodeClient nodeClient; + private final TransportService transportService; + + @Inject + public TransportGetFlamegraphAction(NodeClient nodeClient, TransportService transportService, ActionFilters actionFilters) { + super(GetFlamegraphAction.NAME, transportService, actionFilters, GetStackTracesRequest::new); + this.nodeClient = nodeClient; + this.transportService = transportService; + } + + @Override + protected void doExecute(Task task, GetStackTracesRequest request, ActionListener listener) { + Client client = new ParentTaskAssigningClient(this.nodeClient, transportService.getLocalNode(), task); + long start = System.nanoTime(); + client.execute(GetStackTracesAction.INSTANCE, request, new ActionListener<>() { + @Override + public void onResponse(GetStackTracesResponse response) { + long responseStart = System.nanoTime(); + try { + GetFlamegraphResponse flamegraphResponse = buildFlamegraph(response); + log.debug( + "getFlamegraphAction took [" + + (System.nanoTime() - start) / 1_000_000.0d + + "] ms (processing response: [" + + (System.nanoTime() - responseStart) / 1_000_000.0d + + "] ms." + ); + listener.onResponse(flamegraphResponse); + } catch (Exception ex) { + listener.onFailure(ex); + } + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }); + } + + static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) { + FlamegraphBuilder builder = new FlamegraphBuilder(response.getTotalFrames(), response.getSamplingRate()); + if (response.getTotalFrames() == 0) { + return builder.build(); + } + + SortedMap sortedStacktraces = new TreeMap<>(response.getStackTraces()); + for (Map.Entry st : sortedStacktraces.entrySet()) { + String stackTraceId = st.getKey(); + StackTrace stackTrace = st.getValue(); + int samples = response.getStackTraceEvents().getOrDefault(stackTraceId, 0); + builder.setCurrentNode(0); + builder.addSamplesInclusive(0, samples); + builder.addSamplesExclusive(0, 0); + + int frameCount = stackTrace.frameIds.size(); + for (int i = 0; i < frameCount; i++) { + String frameId = stackTrace.frameIds.get(i); + String fileId = stackTrace.fileIds.get(i); + Integer frameType = stackTrace.typeIds.get(i); + Integer addressOrLine = stackTrace.addressOrLines.get(i); + StackFrame stackFrame = response.getStackFrames().getOrDefault(frameId, EMPTY_STACKFRAME); + String executable = response.getExecutables().getOrDefault(fileId, ""); + + for (Frame frame : stackFrame.frames()) { + String frameGroupId = createFrameGroupId(fileId, addressOrLine, executable, frame.fileName(), frame.functionName()); + + int nodeId; + if (builder.isExists(frameGroupId)) { + nodeId = builder.getNodeId(frameGroupId); + builder.addSamplesInclusive(nodeId, samples); + } else { + nodeId = builder.addNode( + fileId, + frameType, + frame.inline(), + executable, + addressOrLine, + frame.functionName(), + frame.functionOffset(), + frame.fileName(), + frame.lineNumber(), + samples, + frameGroupId + ); + } + if (i == frameCount - 1) { + // Leaf frame: sum up counts for exclusive CPU. + builder.addSamplesExclusive(nodeId, samples); + } + builder.setCurrentNode(nodeId); + } + } + } + return builder.build(); + } + + @SuppressForbidden(reason = "Using pathSeparator constant to extract the filename with low overhead") + private static String getFilename(String fullPath) { + if (fullPath == null || fullPath.isEmpty()) { + return fullPath; + } + int lastSeparatorIdx = fullPath.lastIndexOf(File.pathSeparator); + return lastSeparatorIdx == -1 ? fullPath : fullPath.substring(lastSeparatorIdx + 1); + } + + private static String createFrameGroupId( + String fileId, + Integer addressOrLine, + String exeFilename, + String sourceFilename, + String functionName + ) { + StringBuilder sb = new StringBuilder(); + if (functionName.isEmpty()) { + sb.append(fileId); + sb.append(addressOrLine); + } else { + sb.append(exeFilename); + sb.append(functionName); + sb.append(getFilename(sourceFilename)); + } + return sb.toString(); + } + + private static class FlamegraphBuilder { + private int currentNode = 0; + private int size = 0; + // Map: FrameGroupId -> NodeId + private final List> edges; + private final List fileIds; + private final List frameTypes; + private final List inlineFrames; + private final List fileNames; + private final List addressOrLines; + private final List functionNames; + private final List functionOffsets; + private final List sourceFileNames; + private final List sourceLines; + private final List countInclusive; + private final List countExclusive; + private final double samplingRate; + + FlamegraphBuilder(int frames, double samplingRate) { + // as the number of frames does not account for inline frames we slightly overprovision. + int capacity = (int) (frames * 1.1d); + this.edges = new ArrayList<>(capacity); + this.fileIds = new ArrayList<>(capacity); + this.frameTypes = new ArrayList<>(capacity); + this.inlineFrames = new ArrayList<>(capacity); + this.fileNames = new ArrayList<>(capacity); + this.addressOrLines = new ArrayList<>(capacity); + this.functionNames = new ArrayList<>(capacity); + this.functionOffsets = new ArrayList<>(capacity); + this.sourceFileNames = new ArrayList<>(capacity); + this.sourceLines = new ArrayList<>(capacity); + this.countInclusive = new ArrayList<>(capacity); + this.countExclusive = new ArrayList<>(capacity); + if (frames > 0) { + // root node + int nodeId = this.addNode("", 0, false, "", 0, "", 0, "", 0, 0, null); + this.setCurrentNode(nodeId); + } + this.samplingRate = samplingRate; + } + + // returns the new node's id + public int addNode( + String fileId, + int frameType, + boolean inline, + String fileName, + Integer addressOrLine, + String functionName, + int functionOffset, + String sourceFileName, + int sourceLine, + int samples, + String frameGroupId + ) { + int node = this.size; + this.edges.add(new HashMap<>()); + this.fileIds.add(fileId); + this.frameTypes.add(frameType); + this.inlineFrames.add(inline); + this.fileNames.add(fileName); + this.addressOrLines.add(addressOrLine); + this.functionNames.add(functionName); + this.functionOffsets.add(functionOffset); + this.sourceFileNames.add(sourceFileName); + this.sourceLines.add(sourceLine); + this.countInclusive.add(samples); + this.countExclusive.add(0); + if (frameGroupId != null) { + this.edges.get(currentNode).put(frameGroupId, node); + } + this.size++; + return node; + } + + public void setCurrentNode(int nodeId) { + this.currentNode = nodeId; + } + + public boolean isExists(String frameGroupId) { + return this.edges.get(currentNode).containsKey(frameGroupId); + } + + public int getNodeId(String frameGroupId) { + return this.edges.get(currentNode).get(frameGroupId); + } + + public void addSamplesInclusive(int nodeId, int sampleCount) { + Integer priorSampleCount = this.countInclusive.get(nodeId); + this.countInclusive.set(nodeId, priorSampleCount + sampleCount); + } + + public void addSamplesExclusive(int nodeId, int sampleCount) { + Integer priorSampleCount = this.countExclusive.get(nodeId); + this.countExclusive.set(nodeId, priorSampleCount + sampleCount); + } + + public GetFlamegraphResponse build() { + return new GetFlamegraphResponse( + size, + samplingRate, + edges, + fileIds, + frameTypes, + inlineFrames, + fileNames, + addressOrLines, + functionNames, + functionOffsets, + sourceFileNames, + sourceLines, + countInclusive, + countExclusive + ); + } + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java index 60ecd7f845760..3a3c37b04b3d5 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java @@ -44,7 +44,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -155,7 +154,6 @@ private void searchEventGroupByStackTrace( ) { long start = System.nanoTime(); GetStackTracesResponseBuilder responseBuilder = new GetStackTracesResponseBuilder(); - int exp = eventsIndex.getExponent(); responseBuilder.setSampleRate(eventsIndex.getSampleRate()); client.prepareSearch(eventsIndex.getName()) .setTrackTotalHits(false) @@ -372,55 +370,6 @@ private void retrieveStackTraceDetails( } } - private static class Resampler { - private final boolean requiresResampling; - - private final Random r; - - private final double sampleRate; - - private final double p; - - Resampler(GetStackTracesRequest request, double sampleRate, long totalCount) { - // Manually reduce sample count if totalCount exceeds sampleSize by 10%. - if (totalCount > request.getSampleSize() * 1.1) { - this.requiresResampling = true; - // Make the RNG predictable to get reproducible results. - this.r = new Random(request.hashCode()); - this.sampleRate = sampleRate; - this.p = (double) request.getSampleSize() / totalCount; - } else { - this.requiresResampling = false; - this.r = null; - this.sampleRate = sampleRate; - this.p = 1.0d; - } - } - - public int adjustSampleCount(int originalCount) { - if (requiresResampling) { - int newCount = 0; - for (int i = 0; i < originalCount; i++) { - if (r.nextDouble() < p) { - newCount++; - } - } - if (newCount > 0) { - // Adjust the sample counts from down-sampled to fully sampled. - // Be aware that downsampling drops entries from stackTraceEvents, so that - // the sum of the upscaled count values is less that totalCount. - // This code needs to be refactored to move all scaling into the server - // side, not just the resampling-scaling. - return (int) Math.floor(newCount / (p)); - } else { - return 0; - } - } else { - return originalCount; - } - } - } - /** * Collects stack trace details which are retrieved concurrently and sends a response only when all details are known. */ @@ -458,7 +407,12 @@ public void onStackFramesResponse(MultiGetResponse multiGetItemResponses) { if (frame.getResponse().isExists()) { // Duplicates are expected as we query multiple indices - do a quick pre-check before we deserialize a response if (stackFrames.containsKey(frame.getId()) == false) { - stackFrames.putIfAbsent(frame.getId(), StackFrame.fromSource(frame.getResponse().getSource())); + StackFrame stackFrame = StackFrame.fromSource(frame.getResponse().getSource()); + if (stackFrame.isEmpty() == false) { + stackFrames.putIfAbsent(frame.getId(), stackFrame); + } else { + log.trace("Stack frame with id [{}] has no properties.", frame.getId()); + } } } } diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java new file mode 100644 index 0000000000000..79585986c64e2 --- /dev/null +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java @@ -0,0 +1,108 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.test.ESTestCase; + +import java.util.random.RandomGenerator; + +public class ResamplerTests extends ESTestCase { + + private Resampler createResampler(GetStackTracesRequest request, double sampleRate, long totalCount) { + return new Resampler(request, sampleRate, totalCount) { + @Override + protected RandomGenerator createRandom(GetStackTracesRequest request) { + return DeterministicRandom.of(0.0d, 1.0d); + } + }; + } + + public void testNoResamplingNoSampleRateAdjustment() { + // corresponds to profiling-events-5pow01 + double sampleRate = 1.0d / Math.pow(5.0d, 1); + int requestedSamples = 20_000; + int actualTotalSamples = 10_000; + + GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); + request.setAdjustSampleCount(false); + + Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); + + int actualSamplesSingleTrace = 5_000; + assertEquals(5_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); + } + + public void testNoResamplingButAdjustSampleRate() { + // corresponds to profiling-events-5pow01 + double sampleRate = 1.0d / Math.pow(5.0d, 1); + int requestedSamples = 20_000; + int actualTotalSamples = 10_000; + + GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); + request.setAdjustSampleCount(true); + + Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); + + int actualSamplesSingleTrace = 5_000; + assertEquals(25_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); + } + + public void testResamplingNoSampleRateAdjustment() { + // corresponds to profiling-events-5pow01 + double sampleRate = 1.0d / Math.pow(5.0d, 1); + int requestedSamples = 20_000; + int actualTotalSamples = 40_000; + + GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); + request.setAdjustSampleCount(false); + + Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); + + int actualSamplesSingleTrace = 20_000; + assertEquals(20_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); + } + + public void testResamplingAndSampleRateAdjustment() { + // corresponds to profiling-events-5pow01 + double sampleRate = 1.0d / Math.pow(5.0d, 1); + int requestedSamples = 20_000; + int actualTotalSamples = 40_000; + + GetStackTracesRequest request = new GetStackTracesRequest(requestedSamples, null); + request.setAdjustSampleCount(true); + + Resampler resampler = createResampler(request, sampleRate, actualTotalSamples); + + int actualSamplesSingleTrace = 20_000; + assertEquals(100_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); + } + + private static class DeterministicRandom implements RandomGenerator { + private final double[] values; + private int idx; + + private DeterministicRandom(double... values) { + this.values = values; + this.idx = 0; + } + + public static RandomGenerator of(double... values) { + return new DeterministicRandom(values); + } + + @Override + public long nextLong() { + return Double.doubleToLongBits(nextDouble()); + } + + @Override + public double nextDouble() { + return values[idx++ % values.length]; + } + } +} diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java new file mode 100644 index 0000000000000..2adb41ce45038 --- /dev/null +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.test.ESTestCase; + +import java.util.List; +import java.util.Map; + +public class TransportGetFlamegraphActionTests extends ESTestCase { + public void testCreateFlamegraph() { + GetStackTracesResponse stacktraces = new GetStackTracesResponse( + Map.of( + "2buqP1GpF-TXYmL4USW8gA", + new StackTrace( + List.of(12784352, 19334053, 19336161, 18795859, 18622708, 18619213, 12989721, 13658842, 16339645), + List.of( + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w" + ), + List.of( + "fr28zxcZ2UDasxYuu6dV-wAAAAAAwxLg", + "fr28zxcZ2UDasxYuu6dV-wAAAAABJwOl", + "fr28zxcZ2UDasxYuu6dV-wAAAAABJwvh", + "fr28zxcZ2UDasxYuu6dV-wAAAAABHs1T", + "fr28zxcZ2UDasxYuu6dV-wAAAAABHCj0", + "fr28zxcZ2UDasxYuu6dV-wAAAAABHBtN", + "fr28zxcZ2UDasxYuu6dV-wAAAAAAxjUZ", + "fr28zxcZ2UDasxYuu6dV-wAAAAAA0Gra", + "fr28zxcZ2UDasxYuu6dV-wAAAAAA-VK9" + ), + List.of(3, 3, 3, 3, 3, 3, 3, 3, 3) + ) + ), + Map.of(), + Map.of("fr28zxcZ2UDasxYuu6dV-w", "containerd"), + Map.of("2buqP1GpF-TXYmL4USW8gA", 1), + 9, + 1.0d + ); + GetFlamegraphResponse response = TransportGetFlamegraphAction.buildFlamegraph(stacktraces); + assertNotNull(response); + assertEquals(10, response.getSize()); + assertEquals(1.0d, response.getSamplingRate(), 0.001d); + assertEquals(List.of(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), response.getCountInclusive()); + assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 1), response.getCountExclusive()); + + } +} diff --git a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java index 61c953e7f1c13..fe799b0a590f2 100644 --- a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java +++ b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java @@ -499,6 +499,7 @@ public class Constants { "indices:data/read/mtv[shard]", "indices:data/read/open_point_in_time", "indices:data/read/profiling/stack_traces", + "indices:data/read/profiling/flamegraph", "indices:data/read/rank_eval", "indices:data/read/scroll", "indices:data/read/scroll/clear", From c3dece132d219ddbbf086e59c9ae9b7698181cb2 Mon Sep 17 00:00:00 2001 From: ning pan <33685703+NEUpanning@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:45:28 +0800 Subject: [PATCH 005/155] Add a docs link to the log message about each failing bootstrap check (#99644) Add a docs link to the log message about each failing bootstrap check to help new users to understand failing bootstrap checks. Closes #99614 --- docs/changelog/99644.yaml | 6 + .../bootstrap/EvilBootstrapChecksTests.java | 13 +- .../bootstrap/BootstrapCheck.java | 4 + .../bootstrap/BootstrapChecks.java | 77 +++++++++++- .../elasticsearch/common/ReferenceDocs.java | 22 ++++ .../common/reference-docs-links.json | 24 +++- .../bootstrap/BootstrapChecksTests.java | 117 +++++++++++++----- .../org/elasticsearch/node/NodeTests.java | 13 +- .../core/ssl/TransportTLSBootstrapCheck.java | 6 + .../MachineLearningPackageLoader.java | 6 + .../security/PkiRealmBootstrapCheck.java | 6 + ...ecurityImplicitBehaviorBootstrapCheck.java | 14 ++- .../security/TokenSSLBootstrapCheck.java | 5 + .../RoleMappingFileBootstrapCheck.java | 5 + ...tyImplicitBehaviorBootstrapCheckTests.java | 9 +- .../EncryptSensitiveDataBootstrapCheck.java | 6 + 16 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 docs/changelog/99644.yaml diff --git a/docs/changelog/99644.yaml b/docs/changelog/99644.yaml new file mode 100644 index 0000000000000..10c10448c074c --- /dev/null +++ b/docs/changelog/99644.yaml @@ -0,0 +1,6 @@ +pr: 99644 +summary: Add links to docs from failing bootstrap checks +area: Infra/Node Lifecycle +type: enhancement +issues: [99614] + diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java index eda3c337c98f4..58bf1760551d4 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.bootstrap; import org.apache.logging.log4j.Logger; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.node.NodeValidationException; import org.elasticsearch.test.AbstractBootstrapCheckTestCase; @@ -49,7 +50,17 @@ public void tearDown() throws Exception { public void testEnforceBootstrapChecks() throws NodeValidationException { setEsEnforceBootstrapChecks("true"); - final List checks = Collections.singletonList(context -> BootstrapCheck.BootstrapCheckResult.failure("error")); + final List checks = Collections.singletonList(new BootstrapCheck() { + @Override + public BootstrapCheckResult check(BootstrapContext context) { + return BootstrapCheck.BootstrapCheckResult.failure("error"); + } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } + }); final Logger logger = mock(Logger.class); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java index 75e5fcfa3fa0f..6a32959b7e5f7 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java @@ -8,6 +8,8 @@ package org.elasticsearch.bootstrap; +import org.elasticsearch.common.ReferenceDocs; + import java.util.Objects; /** @@ -59,4 +61,6 @@ default boolean alwaysEnforce() { return false; } + ReferenceDocs referenceDocs(); + } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java index b9610c689f92e..a99ed225b244b 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; import org.elasticsearch.cluster.coordination.ClusterBootstrapService; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; @@ -131,10 +132,11 @@ static void check(final BootstrapContext context, final boolean enforceLimits, f for (final BootstrapCheck check : checks) { final BootstrapCheck.BootstrapCheckResult result = check.check(context); if (result.isFailure()) { + final String message = result.getMessage() + "; for more information see [" + check.referenceDocs() + "]"; if (enforceLimits == false && enforceBootstrapChecks == false && check.alwaysEnforce() == false) { - ignoredErrors.add(result.getMessage()); + ignoredErrors.add(message); } else { - errors.add(result.getMessage()); + errors.add(message); } } } @@ -150,7 +152,9 @@ static void check(final BootstrapContext context, final boolean enforceLimits, f + errors.size() + "] bootstrap checks failed. You must address the points described in the following [" + errors.size() - + "] lines before starting Elasticsearch." + + "] lines before starting Elasticsearch. For more information see [" + + ReferenceDocs.BOOTSTRAP_CHECKS + + "]" ); for (int i = 0; i < errors.size(); i++) { messages.add("bootstrap check failure [" + (i + 1) + "] of [" + errors.size() + "]: " + errors.get(i)); @@ -240,6 +244,11 @@ public BootstrapCheckResult check(BootstrapContext context) { } } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_HEAP_SIZE; + } + // visible for testing long getInitialHeapSize() { return JvmInfo.jvmInfo().getConfiguredInitialHeapSize(); @@ -298,6 +307,11 @@ public final BootstrapCheckResult check(BootstrapContext context) { } } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_FILE_DESCRIPTOR; + } + // visible for testing long getMaxFileDescriptorCount() { return ProcessProbe.getMaxFileDescriptorCount(); @@ -321,6 +335,11 @@ boolean isMemoryLocked() { return Natives.isMemoryLocked(); } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_MEMORY_LOCK; + } + } static class MaxNumberOfThreadsCheck implements BootstrapCheck { @@ -349,6 +368,10 @@ long getMaxNumberOfThreads() { return JNANatives.MAX_NUMBER_OF_THREADS; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_MAX_NUMBER_THREADS; + } } static class MaxSizeVirtualMemoryCheck implements BootstrapCheck { @@ -378,6 +401,10 @@ long getMaxSizeVirtualMemory() { return JNANatives.MAX_SIZE_VIRTUAL_MEMORY; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_MAX_SIZE_VIRTUAL_MEMORY; + } } /** @@ -409,6 +436,10 @@ long getMaxFileSize() { return JNANatives.MAX_FILE_SIZE; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_MAX_FILE_SIZE; + } } static class MaxMapCountCheck implements BootstrapCheck { @@ -478,6 +509,10 @@ static long parseProcSysVmMaxMapCount(final String procSysVmMaxMapCount) throws return Long.parseLong(procSysVmMaxMapCount); } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_MAXIMUM_MAP_COUNT; + } } static class ClientJvmCheck implements BootstrapCheck { @@ -501,6 +536,10 @@ String getVmName() { return JvmInfo.jvmInfo().getVmName(); } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_CLIENT_JVM; + } } /** @@ -529,6 +568,10 @@ String getUseSerialGC() { return JvmInfo.jvmInfo().useSerialGC(); } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_USE_SERIAL_COLLECTOR; + } } /** @@ -551,6 +594,10 @@ boolean isSystemCallFilterInstalled() { return Natives.isSystemCallFilterInstalled(); } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_SYSTEM_CALL_FILTER; + } } abstract static class MightForkCheck implements BootstrapCheck { @@ -579,6 +626,11 @@ public final boolean alwaysEnforce() { return true; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_ONERROR_AND_ONOUTOFMEMORYERROR; + } + } static class OnErrorCheck extends MightForkCheck { @@ -658,6 +710,11 @@ String javaVersion() { return Constants.JAVA_VERSION; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_EARLY_ACCESS; + } + } static class AllPermissionCheck implements BootstrapCheck { @@ -681,6 +738,10 @@ boolean isAllPermissionGranted() { return true; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_ALL_PERMISSION; + } } static class DiscoveryConfiguredCheck implements BootstrapCheck { @@ -703,6 +764,11 @@ public BootstrapCheckResult check(BootstrapContext context) { ) ); } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_DISCOVERY_CONFIGURATION; + } } static class ByteOrderCheck implements BootstrapCheck { @@ -718,5 +784,10 @@ public BootstrapCheckResult check(BootstrapContext context) { ByteOrder nativeByteOrder() { return ByteOrder.nativeOrder(); } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } } } diff --git a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java index 0f60dbff56cfa..1ff42b16252c8 100644 --- a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java +++ b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java @@ -46,6 +46,28 @@ public enum ReferenceDocs { CONCURRENT_REPOSITORY_WRITERS, ARCHIVE_INDICES, HTTP_TRACER, + BOOTSTRAP_CHECK_HEAP_SIZE, + BOOTSTRAP_CHECK_FILE_DESCRIPTOR, + BOOTSTRAP_CHECK_MEMORY_LOCK, + BOOTSTRAP_CHECK_MAX_NUMBER_THREADS, + BOOTSTRAP_CHECK_MAX_FILE_SIZE, + BOOTSTRAP_CHECK_MAX_SIZE_VIRTUAL_MEMORY, + BOOTSTRAP_CHECK_MAXIMUM_MAP_COUNT, + BOOTSTRAP_CHECK_CLIENT_JVM, + BOOTSTRAP_CHECK_USE_SERIAL_COLLECTOR, + BOOTSTRAP_CHECK_SYSTEM_CALL_FILTER, + BOOTSTRAP_CHECK_ONERROR_AND_ONOUTOFMEMORYERROR, + BOOTSTRAP_CHECK_EARLY_ACCESS, + BOOTSTRAP_CHECK_G1GC, + BOOTSTRAP_CHECK_ALL_PERMISSION, + BOOTSTRAP_CHECK_DISCOVERY_CONFIGURATION, + BOOTSTRAP_CHECKS, + BOOTSTRAP_CHECK_ENCRYPT_SENSITIVE_DATA, + BOOTSTRAP_CHECK_PKI_REALM, + BOOTSTRAP_CHECK_ROLE_MAPPINGS, + BOOTSTRAP_CHECK_TLS, + BOOTSTRAP_CHECK_TOKEN_SSL, + BOOTSTRAP_CHECK_SECURITY_MINIMAL_SETUP, // this comment keeps the ';' on the next line so every entry above has a trailing ',' which makes the diff for adding new links cleaner ; diff --git a/server/src/main/resources/org/elasticsearch/common/reference-docs-links.json b/server/src/main/resources/org/elasticsearch/common/reference-docs-links.json index 4de327d203d16..b162aa5b6c31a 100644 --- a/server/src/main/resources/org/elasticsearch/common/reference-docs-links.json +++ b/server/src/main/resources/org/elasticsearch/common/reference-docs-links.json @@ -6,5 +6,27 @@ "SHARD_LOCK_TROUBLESHOOTING": "troubleshooting-unstable-cluster.html#_diagnosing_shardlockobtainfailedexception_failures_2", "CONCURRENT_REPOSITORY_WRITERS": "add-repository.html", "ARCHIVE_INDICES": "archive-indices.html", - "HTTP_TRACER": "modules-network.html#http-rest-request-tracer" + "HTTP_TRACER": "modules-network.html#http-rest-request-tracer", + "BOOTSTRAP_CHECK_HEAP_SIZE": "_heap_size_check.html", + "BOOTSTRAP_CHECK_FILE_DESCRIPTOR": "_file_descriptor_check.html", + "BOOTSTRAP_CHECK_MEMORY_LOCK": "_memory_lock_check.html", + "BOOTSTRAP_CHECK_MAX_NUMBER_THREADS": "max-number-threads-check.html", + "BOOTSTRAP_CHECK_MAX_FILE_SIZE": "_max_file_size_check.html", + "BOOTSTRAP_CHECK_MAX_SIZE_VIRTUAL_MEMORY": "max-size-virtual-memory-check.html", + "BOOTSTRAP_CHECK_MAXIMUM_MAP_COUNT": "_maximum_map_count_check.html", + "BOOTSTRAP_CHECK_CLIENT_JVM": "_client_jvm_check.html", + "BOOTSTRAP_CHECK_USE_SERIAL_COLLECTOR": "_use_serial_collector_check.html", + "BOOTSTRAP_CHECK_SYSTEM_CALL_FILTER": "_system_call_filter_check.html", + "BOOTSTRAP_CHECK_ONERROR_AND_ONOUTOFMEMORYERROR": "_onerror_and_onoutofmemoryerror_checks.html", + "BOOTSTRAP_CHECK_EARLY_ACCESS": "_early_access_check.html", + "BOOTSTRAP_CHECK_G1GC": "_g1gc_check.html", + "BOOTSTRAP_CHECK_ALL_PERMISSION": "_all_permission_check.html", + "BOOTSTRAP_CHECK_DISCOVERY_CONFIGURATION": "_discovery_configuration_check.html", + "BOOTSTRAP_CHECKS": "bootstrap-checks.html", + "BOOTSTRAP_CHECK_ENCRYPT_SENSITIVE_DATA": "bootstrap-checks-xpack.html#_encrypt_sensitive_data_check", + "BOOTSTRAP_CHECK_PKI_REALM": "bootstrap-checks-xpack.html#_pki_realm_check", + "BOOTSTRAP_CHECK_ROLE_MAPPINGS": "bootstrap-checks-xpack.html#_role_mappings_check", + "BOOTSTRAP_CHECK_TLS": "bootstrap-checks-xpack.html#bootstrap-checks-tls", + "BOOTSTRAP_CHECK_TOKEN_SSL": "bootstrap-checks-xpack.html#_token_ssl_check", + "BOOTSTRAP_CHECK_SECURITY_MINIMAL_SETUP": "security-minimal-setup.html" } diff --git a/server/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java b/server/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java index 843a65aa877ac..09ef0b6affc23 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java +++ b/server/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java @@ -12,6 +12,7 @@ import org.apache.lucene.util.Constants; import org.elasticsearch.cluster.coordination.ClusterBootstrapService; import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; @@ -128,10 +129,27 @@ public void testEnforceLimitsWhenPublishingToNonLocalAddress() { } public void testExceptionAggregation() { - final List checks = Arrays.asList( - context -> BootstrapCheck.BootstrapCheckResult.failure("first"), - context -> BootstrapCheck.BootstrapCheckResult.failure("second") - ); + final List checks = Arrays.asList(new BootstrapCheck() { + @Override + public BootstrapCheckResult check(BootstrapContext context) { + return BootstrapCheck.BootstrapCheckResult.failure("first"); + } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } + }, new BootstrapCheck() { + @Override + public BootstrapCheckResult check(BootstrapContext context) { + return BootstrapCheck.BootstrapCheckResult.failure("second"); + } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } + }); final NodeValidationException e = expectThrows( NodeValidationException.class, @@ -146,7 +164,8 @@ public void testExceptionAggregation() { containsString("bootstrap check failure [1] of [2]:"), containsString("first"), containsString("bootstrap check failure [2] of [2]:"), - containsString("second") + containsString("second"), + containsString("For more information see [https://www.elastic.co/guide/en/elasticsearch/reference/") ) ) ); @@ -194,6 +213,7 @@ boolean isMemoryLocked() { "initial heap size [" + initialHeapSize.get() + "] " + "not equal to maximum heap size [" + maxHeapSize.get() + "]" ) ); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); final String memoryLockingMessage = "and prevents memory locking from locking the entire heap"; final Matcher memoryLockingMatcher; if (isMemoryLocked) { @@ -243,6 +263,7 @@ long getMaxFileDescriptorCount() { () -> BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)) ); assertThat(e.getMessage(), containsString("max file descriptors")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); maxFileDescriptorCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE)); @@ -300,6 +321,10 @@ boolean isMemoryLocked() { () -> BootstrapChecks.check(bootstrapContext, true, Collections.singletonList(check)) ); assertThat(e.getMessage(), containsString("memory locking requested for elasticsearch process but memory is not locked")); + assertThat( + e.getMessage(), + containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/") + ); } else { // nothing should happen BootstrapChecks.check(bootstrapContext, true, Collections.singletonList(check)); @@ -322,6 +347,7 @@ long getMaxNumberOfThreads() { () -> BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)) ); assertThat(e.getMessage(), containsString("max number of threads")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); maxNumberOfThreads.set(randomIntBetween(limit + 1, Integer.MAX_VALUE)); @@ -353,6 +379,7 @@ long getRlimInfinity() { () -> BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)) ); assertThat(e.getMessage(), containsString("max size virtual memory")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); maxSizeVirtualMemory.set(rlimInfinity); @@ -383,6 +410,7 @@ long getRlimInfinity() { () -> BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)) ); assertThat(e.getMessage(), containsString("max file size")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); maxFileSize.set(rlimInfinity); @@ -413,6 +441,7 @@ String getVmName() { + "but should be using a server VM for the best performance" ) ); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); vmName.set("Java HotSpot(TM) 32-Bit Server VM"); BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)); @@ -441,6 +470,7 @@ String getUseSerialGC() { + "] or -XX:+UseSerialGC was explicitly specified" ) ); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); useSerialGC.set("false"); BootstrapChecks.check(emptyContext, true, Collections.singletonList(check)); @@ -464,6 +494,7 @@ boolean isSystemCallFilterInstalled() { () -> BootstrapChecks.check(context, true, Collections.singletonList(systemCallFilterEnabledCheck)) ); assertThat(e.getMessage(), containsString("system call filters failed to install; check the logs and fix your configuration")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); isSystemCallFilterInstalled.set(true); BootstrapChecks.check(context, true, Collections.singletonList(systemCallFilterEnabledCheck)); @@ -489,13 +520,13 @@ String message(BootstrapContext context) { } }; - runMightForkTest( - check, - isSystemCallFilterInstalled, - () -> mightFork.set(false), - () -> mightFork.set(true), - e -> assertThat(e.getMessage(), containsString("error")) - ); + runMightForkTest(check, isSystemCallFilterInstalled, () -> mightFork.set(false), () -> mightFork.set(true), e -> { + assertThat(e.getMessage(), containsString("error")); + assertThat( + e.getMessage(), + containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/") + ); + }); } public void testOnErrorCheck() throws NodeValidationException { @@ -521,15 +552,21 @@ String onError() { isSystemCallFilterInstalled, () -> onError.set(randomBoolean() ? "" : null), () -> onError.set(command), - e -> assertThat( - e.getMessage(), - containsString( - "OnError [" - + command - + "] requires forking but is prevented by system call filters;" - + " upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError" - ) - ) + e -> { + assertThat( + e.getMessage(), + containsString( + "OnError [" + + command + + "] requires forking but is prevented by system call filters;" + + " upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError" + ) + ); + assertThat( + e.getMessage(), + containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/") + ); + } ); } @@ -556,16 +593,22 @@ String onOutOfMemoryError() { isSystemCallFilterInstalled, () -> onOutOfMemoryError.set(randomBoolean() ? "" : null), () -> onOutOfMemoryError.set(command), - e -> assertThat( - e.getMessage(), - containsString( - "OnOutOfMemoryError [" - + command - + "]" - + " requires forking but is prevented by system call filters;" - + " upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError" - ) - ) + e -> { + assertThat( + e.getMessage(), + containsString( + "OnOutOfMemoryError [" + + command + + "]" + + " requires forking but is prevented by system call filters;" + + " upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError" + ) + ); + assertThat( + e.getMessage(), + containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/") + ); + } ); } @@ -629,6 +672,7 @@ String javaVersion() { e.getMessage(), containsString("Java version [" + javaVersion.get() + "] is an early-access build, only use release builds") ); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); // if not on an early-access build, nothing should happen javaVersion.set(randomFrom("1.8.0_152", "9")); @@ -651,6 +695,7 @@ boolean isAllPermissionGranted() { () -> BootstrapChecks.check(emptyContext, true, checks) ); assertThat(e, hasToString(containsString("granting the all permission effectively disables security"))); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); // if all permissions are not granted, nothing should happen isAllPermissionGranted.set(false); @@ -668,6 +713,11 @@ public BootstrapCheckResult check(BootstrapContext context) { public boolean alwaysEnforce() { return true; } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } }; final NodeValidationException alwaysEnforced = expectThrows( @@ -678,7 +728,8 @@ public boolean alwaysEnforce() { } public void testDiscoveryConfiguredCheck() throws NodeValidationException { - final List checks = Collections.singletonList(new BootstrapChecks.DiscoveryConfiguredCheck()); + final BootstrapChecks.DiscoveryConfiguredCheck check = new BootstrapChecks.DiscoveryConfiguredCheck(); + final List checks = Collections.singletonList(check); final BootstrapContext zen2Context = createTestContext( Settings.builder().put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), MULTI_NODE_DISCOVERY_TYPE).build(), @@ -713,6 +764,7 @@ public void testDiscoveryConfiguredCheck() throws NodeValidationException { ) ) ); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); CheckedConsumer ensureChecksPass = b -> { final BootstrapContext context = createTestContext( @@ -741,6 +793,7 @@ ByteOrder nativeByteOrder() { () -> BootstrapChecks.check(emptyContext, true, List.of(byteOrderCheck)) ); assertThat(e.getMessage(), containsString("Little-endian native byte order is required to run Elasticsearch")); + assertThat(e.getMessage(), containsString("; for more information see [https://www.elastic.co/guide/en/elasticsearch/reference/")); reference[0] = ByteOrder.LITTLE_ENDIAN; BootstrapChecks.check(emptyContext, true, List.of(byteOrderCheck)); diff --git a/server/src/test/java/org/elasticsearch/node/NodeTests.java b/server/src/test/java/org/elasticsearch/node/NodeTests.java index a7850e8a577c0..87f5fc6e990f6 100644 --- a/server/src/test/java/org/elasticsearch/node/NodeTests.java +++ b/server/src/test/java/org/elasticsearch/node/NodeTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.component.AbstractLifecycleComponent; @@ -93,7 +94,17 @@ public class NodeTests extends ESTestCase { public static class CheckPlugin extends Plugin { - public static final BootstrapCheck CHECK = context -> BootstrapCheck.BootstrapCheckResult.success(); + public static final BootstrapCheck CHECK = new BootstrapCheck() { + @Override + public BootstrapCheckResult check(BootstrapContext context) { + return BootstrapCheck.BootstrapCheckResult.success(); + } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } + }; @Override public List getBootstrapChecks() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TransportTLSBootstrapCheck.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TransportTLSBootstrapCheck.java index 5899736481884..5c5d556181343 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TransportTLSBootstrapCheck.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TransportTLSBootstrapCheck.java @@ -8,6 +8,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.xpack.core.XPackSettings; /** @@ -27,4 +28,9 @@ public BootstrapCheckResult check(BootstrapContext context) { } return BootstrapCheckResult.success(); } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_TLS; + } } diff --git a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java index 46ba695624f60..2afeda1f13512 100644 --- a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java +++ b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.plugins.ActionPlugin; @@ -88,6 +89,11 @@ public BootstrapCheckResult check(BootstrapContext context) { public boolean alwaysEnforce() { return true; } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECKS; + } }); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java index eac48d3fe7950..26112ed11231f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java @@ -8,6 +8,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.ssl.SslConfiguration; import org.elasticsearch.xpack.core.XPackSettings; @@ -78,4 +79,9 @@ private List getSslContextNames(Settings settings) { public boolean alwaysEnforce() { return true; } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_PKI_REALM; + } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheck.java index ab9aa32c9b859..c6396f886b4bc 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheck.java @@ -10,6 +10,7 @@ import org.elasticsearch.Version; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.env.NodeMetadata; import org.elasticsearch.license.ClusterStateLicenseService; import org.elasticsearch.license.License; @@ -44,11 +45,9 @@ public BootstrapCheckResult check(BootstrapContext context) { + "] has changed in the current version. " + " Security features were implicitly disabled for this node but they would now be enabled, possibly" + " preventing access to the node. " - + "See https://www.elastic.co/guide/en/elasticsearch/reference/" - + Version.CURRENT.major - + "." - + Version.CURRENT.minor - + "/security-minimal-setup.html to configure security, or explicitly disable security by " + + "See " + + this.referenceDocs() + + " to configure security, or explicitly disable security by " + "setting [xpack.security.enabled] to \"false\" in elasticsearch.yml before restarting the node." ); } @@ -59,4 +58,9 @@ public BootstrapCheckResult check(BootstrapContext context) { public boolean alwaysEnforce() { return true; } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_SECURITY_MINIMAL_SETUP; + } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/TokenSSLBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/TokenSSLBootstrapCheck.java index 1c2fbb3df425b..7611ef8d258ce 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/TokenSSLBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/TokenSSLBootstrapCheck.java @@ -8,6 +8,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.xpack.core.XPackSettings; import java.util.Locale; @@ -35,4 +36,8 @@ public BootstrapCheckResult check(BootstrapContext context) { } } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_TOKEN_SSL; + } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/RoleMappingFileBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/RoleMappingFileBootstrapCheck.java index b76124d5c4631..d70552f016bbf 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/RoleMappingFileBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/RoleMappingFileBootstrapCheck.java @@ -9,6 +9,7 @@ import org.apache.logging.log4j.LogManager; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.support.DnRoleMapperSettings; @@ -51,4 +52,8 @@ public static BootstrapCheck create(RealmConfig realmConfig) { return null; } + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_ROLE_MAPPINGS; + } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java index 3a1ae84b7c682..9775e461c4165 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityImplicitBehaviorBootstrapCheckTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.Version; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.NodeMetadata; @@ -53,11 +54,9 @@ public void testFailureUpgradeFrom7xWithImplicitSecuritySettings() throws Except + "] has changed in the current version. " + " Security features were implicitly disabled for this node but they would now be enabled, possibly" + " preventing access to the node. " - + "See https://www.elastic.co/guide/en/elasticsearch/reference/" - + Version.CURRENT.major - + "." - + Version.CURRENT.minor - + "/security-minimal-setup.html to configure security, or explicitly disable security by " + + "See " + + ReferenceDocs.BOOTSTRAP_CHECK_SECURITY_MINIMAL_SETUP + + " to configure security, or explicitly disable security by " + "setting [xpack.security.enabled] to \"false\" in elasticsearch.yml before restarting the node." ) ); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java index 555787f577efe..430d6985e3444 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java @@ -8,6 +8,7 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; +import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.watcher.WatcherField; @@ -50,4 +51,9 @@ public BootstrapCheckResult check(BootstrapContext context) { public boolean alwaysEnforce() { return true; } + + @Override + public ReferenceDocs referenceDocs() { + return ReferenceDocs.BOOTSTRAP_CHECK_ENCRYPT_SENSITIVE_DATA; + } } From 2b4ce4d768ed090c0773a6eb529c60cabdbc553a Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 21 Sep 2023 07:48:48 +0100 Subject: [PATCH 006/155] Wait for cluster state in recovery (#99193) A peer recovery is triggered by the application of a cluster state on the target node, which creates a local `IndexShard` instance and then reaches out to the source node to coordinate the recovery. If the source node has not applied the same cluster state yet then it will respond with a `DelayRecoveryException` which tells the target node to wait a little and then retry. With this commit we now wait on the source node until a fresh enough cluster state is applied, avoiding the need for the timed wait on the target node (and another network round trip) before the recovery can begin. --- docs/changelog/99193.yaml | 5 + .../indices/recovery/IndexRecoveryIT.java | 133 ++++++++++++ .../org/elasticsearch/TransportVersions.java | 1 + .../elasticsearch/index/shard/IndexShard.java | 5 +- .../elasticsearch/indices/IndicesService.java | 8 +- .../cluster/IndicesClusterStateService.java | 10 +- .../PeerRecoverySourceClusterStateDelay.java | 72 +++++++ .../recovery/PeerRecoverySourceService.java | 27 ++- .../recovery/PeerRecoveryTargetService.java | 12 +- .../recovery/RecoveriesCollection.java | 2 + .../indices/recovery/RecoveryTarget.java | 29 ++- .../recovery/StartRecoveryRequest.java | 21 +- .../StatelessPrimaryRelocationAction.java | 22 +- .../java/org/elasticsearch/node/Node.java | 8 +- .../IndexLevelReplicationTests.java | 4 +- .../RecoveryDuringReplicationTests.java | 6 +- .../index/shard/IndexShardTests.java | 200 ++++++++++-------- ...actIndicesClusterStateServiceTestCase.java | 11 +- .../PeerRecoverySourceServiceTests.java | 2 + .../PeerRecoveryTargetServiceTests.java | 18 +- .../recovery/RecoverySourceHandlerTests.java | 3 +- .../indices/recovery/RecoveryTests.java | 4 +- .../recovery/StartRecoveryRequestTests.java | 30 +-- ...StatelessPrimaryRelocationActionTests.java | 24 ++- .../recovery/RecoveriesCollectionTests.java | 2 +- .../snapshots/SnapshotResiliencyTests.java | 1 + .../ESIndexLevelReplicationTestCase.java | 2 +- .../index/shard/IndexShardTestCase.java | 2 +- .../org/elasticsearch/test/ESTestCase.java | 4 + .../ShardFollowTaskReplicationTests.java | 2 +- 30 files changed, 505 insertions(+), 165 deletions(-) create mode 100644 docs/changelog/99193.yaml create mode 100644 server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceClusterStateDelay.java diff --git a/docs/changelog/99193.yaml b/docs/changelog/99193.yaml new file mode 100644 index 0000000000000..9db646dc80435 --- /dev/null +++ b/docs/changelog/99193.yaml @@ -0,0 +1,5 @@ +pr: 99193 +summary: Wait for cluster state in recovery +area: Recovery +type: enhancement +issues: [] diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index c09cc7245074d..7ba3b5814eb4b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -24,6 +24,7 @@ import org.apache.lucene.search.Weight; import org.apache.lucene.util.SetOnce; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; @@ -36,10 +37,15 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.ActiveShardCount; +import org.elasticsearch.action.support.ChannelActionListener; import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; import org.elasticsearch.action.support.replication.ReplicationResponse; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.coordination.ApplyCommitRequest; +import org.elasticsearch.cluster.coordination.Coordinator; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; @@ -57,6 +63,7 @@ import org.elasticsearch.cluster.routing.allocation.command.AllocateEmptyPrimaryAllocationCommand; import org.elasticsearch.cluster.routing.allocation.command.MoveAllocationCommand; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; import org.elasticsearch.common.lucene.search.Queries; @@ -64,6 +71,10 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.CollectionUtils; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.core.AbstractRefCounted; +import org.elasticsearch.core.RefCounted; +import org.elasticsearch.core.Releasable; import org.elasticsearch.gateway.ReplicaShardAllocatorIT; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; @@ -104,6 +115,7 @@ import org.elasticsearch.test.engine.MockEngineSupport; import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.transport.MockTransportService; +import org.elasticsearch.transport.TestTransportChannel; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentType; @@ -136,6 +148,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; @@ -1614,6 +1627,126 @@ public void testReservesBytesDuringPeerRecoveryPhaseOne() throws Exception { ); } + public void testWaitForClusterStateToBeAppliedOnSourceNode() throws Exception { + internalCluster().startMasterOnlyNode(); + final var primaryNode = internalCluster().startDataOnlyNode(); + String indexName = "test-index"; + createIndex(indexName, indexSettings(1, 0).build()); + ensureGreen(indexName); + final List indexRequests = IntStream.range(0, between(10, 500)) + .mapToObj(n -> client().prepareIndex(indexName).setSource("foo", "bar")) + .toList(); + indexRandom(randomBoolean(), true, true, indexRequests); + assertThat(indicesAdmin().prepareFlush(indexName).get().getFailedShards(), equalTo(0)); + + final var replicaNode = internalCluster().startDataOnlyNode(); + + final long initialClusterStateVersion = clusterService().state().version(); + + // Helper class to encapsulate the sync mechanism that delays applying cluster states on the primary node until the replica gives + // the go-ahead. + class ClusterStateSyncListeners implements Releasable { + private final Map> clusterStateBarriers = ConcurrentCollections.newConcurrentMap(); + private final SubscribableListener startRecoveryListener = new SubscribableListener<>(); + + private final CountDownLatch completeLatch = new CountDownLatch(1); + private final RefCounted refCounted = AbstractRefCounted.of(completeLatch::countDown); + private final List cleanup = new ArrayList<>(2); + + @Override + public void close() { + refCounted.decRef(); + safeAwait(completeLatch); + cleanup.forEach(Runnable::run); + clusterStateBarriers.values().forEach(l -> l.onResponse(null)); + } + + void addCleanup(Runnable runnable) { + cleanup.add(runnable); + } + + SubscribableListener getStateApplyDelayListener(long clusterStateVersion) { + assertThat(clusterStateVersion, greaterThanOrEqualTo(initialClusterStateVersion)); + if (refCounted.tryIncRef()) { + try { + return clusterStateBarriers.computeIfAbsent(clusterStateVersion, ignored -> new SubscribableListener<>()); + } finally { + refCounted.decRef(); + } + } else { + return SubscribableListener.newSucceeded(null); + } + } + + void onStartRecovery() { + Thread.yield(); + assertFalse(startRecoveryListener.isDone()); + startRecoveryListener.onResponse(null); + } + + public void delayUntilRecoveryStart(SubscribableListener listener) { + assertFalse(startRecoveryListener.isDone()); + startRecoveryListener.addListener(listener); + } + } + + try (var clusterStateSyncListeners = new ClusterStateSyncListeners()) { + final var primaryNodeTransportService = (MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode + ); + primaryNodeTransportService.addRequestHandlingBehavior( + Coordinator.COMMIT_STATE_ACTION_NAME, + (handler, request, channel, task) -> { + assertThat(request, instanceOf(ApplyCommitRequest.class)); + clusterStateSyncListeners.getStateApplyDelayListener(((ApplyCommitRequest) request).getVersion()) + .addListener( + ActionListener.wrap(ignored -> handler.messageReceived(request, channel, task), e -> fail(e, "unexpected")) + ); + } + ); + primaryNodeTransportService.addRequestHandlingBehavior( + PeerRecoverySourceService.Actions.START_RECOVERY, + (handler, request, channel, task) -> { + assertThat(request, instanceOf(StartRecoveryRequest.class)); + assertThat(((StartRecoveryRequest) request).clusterStateVersion(), greaterThan(initialClusterStateVersion)); + handler.messageReceived( + request, + new TestTransportChannel( + new ChannelActionListener<>(channel).delegateResponse( + (l, e) -> fail(e, "recovery should succeed on first attempt") + ) + ), + task + ); + clusterStateSyncListeners.onStartRecovery(); + } + ); + clusterStateSyncListeners.addCleanup(primaryNodeTransportService::clearInboundRules); + + final var replicaClusterService = internalCluster().getInstance(ClusterService.class, replicaNode); + final ClusterStateListener clusterStateListener = event -> { + final var primaryProceedListener = clusterStateSyncListeners.getStateApplyDelayListener(event.state().version()); + final var indexRoutingTable = event.state().routingTable().index(indexName); + assertNotNull(indexRoutingTable); + final var indexShardRoutingTable = indexRoutingTable.shard(0); + if (indexShardRoutingTable.size() == 2 && indexShardRoutingTable.getAllInitializingShards().isEmpty() == false) { + // this is the cluster state update which starts the recovery, so delay the primary node application until recovery + // has started + clusterStateSyncListeners.delayUntilRecoveryStart(primaryProceedListener); + } else { + // this is some other cluster state update, so we must let it proceed now + primaryProceedListener.onResponse(null); + } + }; + replicaClusterService.addListener(clusterStateListener); + clusterStateSyncListeners.addCleanup(() -> replicaClusterService.removeListener(clusterStateListener)); + + updateIndexSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1), indexName); + ensureGreen(indexName); + } + } + private void assertGlobalCheckpointIsStableAndSyncedInAllNodes(String indexName, List nodes, int shard) throws Exception { assertThat(nodes, is(not(empty()))); diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 5c28fa17b7b3e..170f9140bae21 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -142,6 +142,7 @@ static TransportVersion def(int id) { public static final TransportVersion NODE_INFO_INDEX_VERSION_ADDED = def(8_500_075); public static final TransportVersion FIRST_NEW_ID_LAYOUT = def(8_501_00_0); public static final TransportVersion COMMIT_PRIMARY_TERM_GENERATION = def(8_501_00_1); + public static final TransportVersion WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED = def(8_502_00_0); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index c4ef440a921e3..3adee7643990b 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -3090,7 +3090,8 @@ public void startRecovery( PeerRecoveryTargetService.RecoveryListener recoveryListener, RepositoriesService repositoriesService, BiConsumer> mappingUpdateConsumer, - IndicesService indicesService + IndicesService indicesService, + long clusterStateVersion ) { // TODO: Create a proper object to encapsulate the recovery context // all of the current methods here follow a pattern of: @@ -3114,7 +3115,7 @@ public void startRecovery( case PEER -> { try { markAsRecovering("from " + recoveryState.getSourceNode(), recoveryState); - recoveryTargetService.startRecovery(this, recoveryState.getSourceNode(), recoveryListener); + recoveryTargetService.startRecovery(this, recoveryState.getSourceNode(), clusterStateVersion, recoveryListener); } catch (Exception e) { failShard("corrupted preexisting index", e); recoveryListener.onRecoveryFailure(new RecoveryFailedException(recoveryState, null, e), true); diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesService.java b/server/src/main/java/org/elasticsearch/indices/IndicesService.java index b34086ddb5b77..34e74c87aac94 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesService.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesService.java @@ -879,7 +879,7 @@ public synchronized void verifyIndexMetadata(IndexMetadata metadata, IndexMetada } @Override - public IndexShard createShard( + public void createShard( final ShardRouting shardRouting, final PeerRecoveryTargetService recoveryTargetService, final PeerRecoveryTargetService.RecoveryListener recoveryListener, @@ -888,7 +888,8 @@ public IndexShard createShard( final GlobalCheckpointSyncer globalCheckpointSyncer, final RetentionLeaseSyncer retentionLeaseSyncer, final DiscoveryNode targetNode, - final DiscoveryNode sourceNode + final DiscoveryNode sourceNode, + long clusterStateVersion ) throws IOException { Objects.requireNonNull(retentionLeaseSyncer); ensureChangesAllowed(); @@ -911,8 +912,7 @@ public IndexShard createShard( .masterNodeTimeout(TimeValue.MAX_VALUE), new ThreadedActionListener<>(threadPool.generic(), listener.map(ignored -> null)) ); - }, this); - return indexShard; + }, this, clusterStateVersion); } @Override diff --git a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java index d05a5ea377520..39a302963d3d1 100644 --- a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java +++ b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java @@ -645,7 +645,8 @@ private void createShardWhenLockAvailable( this::updateGlobalCheckpointForShard, retentionLeaseSyncer, originalState.nodes().getLocalNode(), - sourceNode + sourceNode, + originalState.version() ); listener.onResponse(true); } catch (ShardLockObtainFailedException e) { @@ -1090,10 +1091,10 @@ U createIndex(IndexMetadata indexMetadata, List builtInIndex * @param retentionLeaseSyncer a callback when this shard syncs retention leases * @param targetNode the node where this shard will be recovered * @param sourceNode the source node to recover this shard from (it might be null) - * @return a new shard + * @param clusterStateVersion the cluster state version in which the shard was created * @throws IOException if an I/O exception occurs when creating the shard */ - T createShard( + void createShard( ShardRouting shardRouting, PeerRecoveryTargetService recoveryTargetService, PeerRecoveryTargetService.RecoveryListener recoveryListener, @@ -1102,7 +1103,8 @@ T createShard( GlobalCheckpointSyncer globalCheckpointSyncer, RetentionLeaseSyncer retentionLeaseSyncer, DiscoveryNode targetNode, - @Nullable DiscoveryNode sourceNode + @Nullable DiscoveryNode sourceNode, + long clusterStateVersion ) throws IOException; /** diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceClusterStateDelay.java b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceClusterStateDelay.java new file mode 100644 index 0000000000000..6610447d488c2 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceClusterStateDelay.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.indices.recovery; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.SubscribableListener; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +public class PeerRecoverySourceClusterStateDelay { + private PeerRecoverySourceClusterStateDelay() {} + + private static final Logger logger = LogManager.getLogger(PeerRecoverySourceClusterStateDelay.class); + + /** + * Waits for the given cluster state version to be applied locally before proceeding with recovery + */ + public static void ensureClusterStateVersion( + long clusterStateVersion, + ClusterService clusterService, + Executor executor, + ThreadContext threadContext, + ActionListener listener, + Consumer> proceedWithRecovery + ) { + if (clusterStateVersion <= clusterService.state().version()) { + // either our locally-applied cluster state is already fresh enough, or request.clusterStateVersion() == 0 for bwc + proceedWithRecovery.accept(listener); + } else { + logger.debug("delaying {} until application of cluster state version {}", proceedWithRecovery, clusterStateVersion); + final var waitListener = new SubscribableListener(); + final var clusterStateVersionListener = new ClusterStateListener() { + @Override + public void clusterChanged(ClusterChangedEvent event) { + if (clusterStateVersion <= event.state().version()) { + waitListener.onResponse(null); + } + } + + @Override + public String toString() { + return "ClusterStateListener for " + proceedWithRecovery; + } + }; + clusterService.addListener(clusterStateVersionListener); + waitListener.addListener(ActionListener.running(() -> clusterService.removeListener(clusterStateVersionListener))); + if (clusterStateVersion <= clusterService.state().version()) { + waitListener.onResponse(null); + } + waitListener.addListener( + listener.delegateFailureAndWrap((l, ignored) -> proceedWithRecovery.accept(l)), + executor, + threadContext + ); + // NB no timeout. If we never apply the fresh cluster state then eventually we leave the cluster which removes the recovery + // from the routing table so the target shard will fail. + } + } +} diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java index eac119f920f6a..fbbd0655e9f10 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoverySourceService.java @@ -21,7 +21,6 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.FutureUtils; import org.elasticsearch.core.Nullable; @@ -43,6 +42,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.function.Consumer; /** * The source recovery accepts recovery requests from other peer shards and start the recovery process from this @@ -59,20 +59,22 @@ public static class Actions { private final TransportService transportService; private final IndicesService indicesService; + private final ClusterService clusterService; private final RecoverySettings recoverySettings; private final RecoveryPlannerService recoveryPlannerService; final OngoingRecoveries ongoingRecoveries = new OngoingRecoveries(); - @Inject public PeerRecoverySourceService( TransportService transportService, IndicesService indicesService, + ClusterService clusterService, RecoverySettings recoverySettings, RecoveryPlannerService recoveryPlannerService ) { this.transportService = transportService; this.indicesService = indicesService; + this.clusterService = clusterService; this.recoverySettings = recoverySettings; this.recoveryPlannerService = recoveryPlannerService; // When the target node wants to start a peer recovery it sends a START_RECOVERY request to the source @@ -132,6 +134,27 @@ public void clusterChanged(ClusterChangedEvent event) { } private void recover(StartRecoveryRequest request, Task task, ActionListener listener) { + PeerRecoverySourceClusterStateDelay.ensureClusterStateVersion( + request.clusterStateVersion(), + clusterService, + transportService.getThreadPool().generic(), + transportService.getThreadPool().getThreadContext(), + listener, + new Consumer<>() { + @Override + public void accept(ActionListener l) { + recoverWithFreshClusterState(request, task, l); + } + + @Override + public String toString() { + return "recovery [" + request + "]"; + } + } + ); + } + + private void recoverWithFreshClusterState(StartRecoveryRequest request, Task task, ActionListener listener) { final IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); final IndexShard shard = indexService.getShard(request.shardId().id()); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java index b3b23ac14d158..2cdd383114497 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java @@ -228,12 +228,18 @@ public void beforeIndexShardClosed(ShardId shardId, @Nullable IndexShard indexSh } } - public void startRecovery(final IndexShard indexShard, final DiscoveryNode sourceNode, final RecoveryListener listener) { + public void startRecovery( + final IndexShard indexShard, + final DiscoveryNode sourceNode, + final long clusterStateVersion, + final RecoveryListener listener + ) { final Releasable snapshotFileDownloadsPermit = tryAcquireSnapshotDownloadPermits(); // create a new recovery status, and process... final long recoveryId = onGoingRecoveries.startRecovery( indexShard, sourceNode, + clusterStateVersion, snapshotFilesProvider, listener, recoverySettings.activityTimeout(), @@ -319,7 +325,8 @@ private void doRecovery(final long recoveryId, final StartRecoveryRequest preExi recoveryId, indexShard.shardId(), transportService.getLocalNode(), - indexShard.routingEntry().allocationId().getId() + indexShard.routingEntry().allocationId().getId(), + recoveryTarget.clusterStateVersion() ), new ActionListener<>() { @Override @@ -455,6 +462,7 @@ public static StartRecoveryRequest getStartRecoveryRequest( recoveryTarget.indexShard().routingEntry().allocationId().getId(), recoveryTarget.sourceNode(), localNode, + recoveryTarget.clusterStateVersion(), metadataSnapshot, recoveryTarget.state().getPrimary(), recoveryTarget.recoveryId(), diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveriesCollection.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveriesCollection.java index cb73d104078dc..0eace1b8fb220 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveriesCollection.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveriesCollection.java @@ -54,6 +54,7 @@ public RecoveriesCollection(Logger logger, ThreadPool threadPool) { public long startRecovery( IndexShard indexShard, DiscoveryNode sourceNode, + long clusterStateVersion, SnapshotFilesProvider snapshotFilesProvider, PeerRecoveryTargetService.RecoveryListener listener, TimeValue activityTimeout, @@ -62,6 +63,7 @@ public long startRecovery( RecoveryTarget recoveryTarget = new RecoveryTarget( indexShard, sourceNode, + clusterStateVersion, snapshotFilesProvider, snapshotFileDownloadsPermit, listener diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index aace3d35efb53..4f0d3b7d798cc 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -70,6 +70,7 @@ public class RecoveryTarget extends AbstractRefCounted implements RecoveryTarget private final long recoveryId; private final IndexShard indexShard; private final DiscoveryNode sourceNode; + private final long clusterStateVersion; private final SnapshotFilesProvider snapshotFilesProvider; private volatile MultiFileWriter multiFileWriter; private final RecoveryRequestTracker requestTracker = new RecoveryRequestTracker(); @@ -94,16 +95,18 @@ public class RecoveryTarget extends AbstractRefCounted implements RecoveryTarget /** * Creates a new recovery target object that represents a recovery to the provided shard. * - * @param indexShard local shard where we want to recover to - * @param sourceNode source node of the recovery where we recover from - * @param snapshotFileDownloadsPermit a permit that allows to download files from a snapshot, - * limiting the concurrent snapshot file downloads per node - * preventing the exhaustion of repository resources. - * @param listener called when recovery is completed/failed + * @param indexShard local shard where we want to recover to + * @param sourceNode source node of the recovery where we recover from + * @param clusterStateVersion version of the cluster state that initiated the recovery + * @param snapshotFileDownloadsPermit a permit that allows to download files from a snapshot, + * limiting the concurrent snapshot file downloads per node + * preventing the exhaustion of repository resources. + * @param listener called when recovery is completed/failed */ public RecoveryTarget( IndexShard indexShard, DiscoveryNode sourceNode, + long clusterStateVersion, SnapshotFilesProvider snapshotFilesProvider, @Nullable Releasable snapshotFileDownloadsPermit, PeerRecoveryTargetService.RecoveryListener listener @@ -114,6 +117,7 @@ public RecoveryTarget( this.logger = Loggers.getLogger(getClass(), indexShard.shardId()); this.indexShard = indexShard; this.sourceNode = sourceNode; + this.clusterStateVersion = clusterStateVersion; this.snapshotFilesProvider = snapshotFilesProvider; this.snapshotFileDownloadsPermit = snapshotFileDownloadsPermit; this.shardId = indexShard.shardId(); @@ -149,7 +153,14 @@ public RecoveryTarget retryCopy() { // get released after the retry copy is created Releasable snapshotFileDownloadsPermitCopy = snapshotFileDownloadsPermit; snapshotFileDownloadsPermit = null; - return new RecoveryTarget(indexShard, sourceNode, snapshotFilesProvider, snapshotFileDownloadsPermitCopy, listener); + return new RecoveryTarget( + indexShard, + sourceNode, + clusterStateVersion, + snapshotFilesProvider, + snapshotFileDownloadsPermitCopy, + listener + ); } @Nullable @@ -174,6 +185,10 @@ public DiscoveryNode sourceNode() { return this.sourceNode; } + public long clusterStateVersion() { + return clusterStateVersion; + } + public RecoveryState state() { return indexShard.recoveryState(); } diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java b/server/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java index 8ed47b448b749..4ace9ab1bc28d 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/StartRecoveryRequest.java @@ -8,6 +8,7 @@ package org.elasticsearch.indices.recovery; +import org.elasticsearch.TransportVersions; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; @@ -29,6 +30,7 @@ public class StartRecoveryRequest extends TransportRequest { private final String targetAllocationId; private final DiscoveryNode sourceNode; private final DiscoveryNode targetNode; + private final long clusterStateVersion; private final Store.MetadataSnapshot metadataSnapshot; private final boolean primaryRelocation; private final long startingSeqNo; @@ -41,6 +43,11 @@ public StartRecoveryRequest(StreamInput in) throws IOException { targetAllocationId = in.readString(); sourceNode = new DiscoveryNode(in); targetNode = new DiscoveryNode(in); + if (in.getTransportVersion().onOrAfter(TransportVersions.WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED)) { + clusterStateVersion = in.readVLong(); + } else { + clusterStateVersion = 0L; // bwc: do not wait for cluster state to be applied + } metadataSnapshot = Store.MetadataSnapshot.readFrom(in); primaryRelocation = in.readBoolean(); startingSeqNo = in.readLong(); @@ -58,6 +65,7 @@ public StartRecoveryRequest(StreamInput in) throws IOException { * @param targetAllocationId the allocation id of the target shard * @param sourceNode the source node to remover from * @param targetNode the target node to recover to + * @param clusterStateVersion the cluster state version which initiated the recovery * @param metadataSnapshot the Lucene metadata * @param primaryRelocation whether or not the recovery is a primary relocation * @param recoveryId the recovery ID @@ -69,12 +77,14 @@ public StartRecoveryRequest( final String targetAllocationId, final DiscoveryNode sourceNode, final DiscoveryNode targetNode, + final long clusterStateVersion, final Store.MetadataSnapshot metadataSnapshot, final boolean primaryRelocation, final long recoveryId, final long startingSeqNo, final boolean canDownloadSnapshotFiles ) { + this.clusterStateVersion = clusterStateVersion; this.recoveryId = recoveryId; this.shardId = shardId; this.targetAllocationId = targetAllocationId; @@ -108,6 +118,10 @@ public DiscoveryNode targetNode() { return targetNode; } + public long clusterStateVersion() { + return clusterStateVersion; + } + public boolean isPrimaryRelocation() { return primaryRelocation; } @@ -129,11 +143,13 @@ public String getDescription() { return Strings.format( """ recovery of %s to %s \ - [recoveryId=%d, targetAllocationId=%s, startingSeqNo=%d, primaryRelocation=%s, canDownloadSnapshotFiles=%s]""", + [recoveryId=%d, targetAllocationId=%s, clusterStateVersion=%d, startingSeqNo=%d, \ + primaryRelocation=%s, canDownloadSnapshotFiles=%s]""", shardId, targetNode.descriptionWithoutAttributes(), recoveryId, targetAllocationId, + clusterStateVersion, startingSeqNo, primaryRelocation, canDownloadSnapshotFiles @@ -148,6 +164,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(targetAllocationId); sourceNode.writeTo(out); targetNode.writeTo(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED)) { + out.writeVLong(clusterStateVersion); + } // else bwc: just omit it, the receiver doesn't wait for a cluster state anyway metadataSnapshot.writeTo(out); out.writeBoolean(primaryRelocation); out.writeLong(startingSeqNo); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationAction.java b/server/src/main/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationAction.java index 1c40d40456014..eed6a1d02ae16 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationAction.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationAction.java @@ -8,6 +8,7 @@ package org.elasticsearch.indices.recovery; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; @@ -33,12 +34,14 @@ public static class Request extends ActionRequest { private final ShardId shardId; private final DiscoveryNode targetNode; private final String targetAllocationId; + private final long clusterStateVersion; - public Request(long recoveryId, ShardId shardId, DiscoveryNode targetNode, String targetAllocationId) { + public Request(long recoveryId, ShardId shardId, DiscoveryNode targetNode, String targetAllocationId, long clusterStateVersion) { this.recoveryId = recoveryId; this.shardId = shardId; this.targetNode = targetNode; this.targetAllocationId = targetAllocationId; + this.clusterStateVersion = clusterStateVersion; } public Request(StreamInput in) throws IOException { @@ -47,6 +50,11 @@ public Request(StreamInput in) throws IOException { shardId = new ShardId(in); targetNode = new DiscoveryNode(in); targetAllocationId = in.readString(); + if (in.getTransportVersion().onOrAfter(TransportVersions.WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED)) { + clusterStateVersion = in.readVLong(); + } else { + clusterStateVersion = 0L; // temporary bwc: do not wait for cluster state to be applied + } } @Override @@ -61,6 +69,9 @@ public void writeTo(StreamOutput out) throws IOException { shardId.writeTo(out); targetNode.writeTo(out); out.writeString(targetAllocationId); + if (out.getTransportVersion().onOrAfter(TransportVersions.WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED)) { + out.writeVLong(clusterStateVersion); + } // temporary bwc: just omit it, the receiver doesn't wait for a cluster state anyway } public long recoveryId() { @@ -79,6 +90,10 @@ public String targetAllocationId() { return targetAllocationId; } + public long clusterStateVersion() { + return clusterStateVersion; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -87,12 +102,13 @@ public boolean equals(Object o) { return recoveryId == request.recoveryId && shardId.equals(request.shardId) && targetNode.equals(request.targetNode) - && targetAllocationId.equals(request.targetAllocationId); + && targetAllocationId.equals(request.targetAllocationId) + && clusterStateVersion == request.clusterStateVersion; } @Override public int hashCode() { - return Objects.hash(recoveryId, shardId, targetNode, targetAllocationId); + return Objects.hash(recoveryId, shardId, targetNode, targetAllocationId, clusterStateVersion); } } } diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 29044084d027d..198abe36e1452 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -1106,7 +1106,13 @@ protected Node( final SnapshotFilesProvider snapshotFilesProvider = new SnapshotFilesProvider(repositoryService); b.bind(PeerRecoverySourceService.class) .toInstance( - new PeerRecoverySourceService(transportService, indicesService, recoverySettings, recoveryPlannerService) + new PeerRecoverySourceService( + transportService, + indicesService, + clusterService, + recoverySettings, + recoveryPlannerService + ) ); b.bind(PeerRecoveryTargetService.class) .toInstance( diff --git a/server/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java b/server/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java index 66b130ca69bc8..64c70f93f8a3f 100644 --- a/server/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java +++ b/server/src/test/java/org/elasticsearch/index/replication/IndexLevelReplicationTests.java @@ -116,7 +116,7 @@ public void run() { IndexShard replica = shards.addReplica(); Future future = shards.asyncRecoverReplica( replica, - (indexShard, node) -> new RecoveryTarget(indexShard, node, null, null, recoveryListener) { + (indexShard, node) -> new RecoveryTarget(indexShard, node, 0L, null, null, recoveryListener) { @Override public void cleanFiles( int totalTranslogOps, @@ -199,7 +199,7 @@ public IndexResult index(Index op) throws IOException { IndexShard replica = shards.addReplica(); Future fut = shards.asyncRecoverReplica( replica, - (shard, node) -> new RecoveryTarget(shard, node, null, null, recoveryListener) { + (shard, node) -> new RecoveryTarget(shard, node, 0L, null, null, recoveryListener) { @Override public void prepareForTranslogOperations(int totalTranslogOps, ActionListener listener) { try { diff --git a/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java b/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java index b7a4c5219b68e..6773b06729a8e 100644 --- a/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java +++ b/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java @@ -452,7 +452,7 @@ protected EngineFactory getEngineFactory(ShardRouting routing) { AtomicBoolean recoveryDone = new AtomicBoolean(false); final Future recoveryFuture = shards.asyncRecoverReplica(newReplica, (indexShard, node) -> { recoveryStart.countDown(); - return new RecoveryTarget(indexShard, node, null, null, recoveryListener) { + return new RecoveryTarget(indexShard, node, 0L, null, null, recoveryListener) { @Override public void finalizeRecovery(long globalCheckpoint, long trimAboveSeqNo, ActionListener listener) { recoveryDone.set(true); @@ -506,7 +506,7 @@ protected EngineFactory getEngineFactory(final ShardRouting routing) { final IndexShard replica = shards.addReplica(); final Future recoveryFuture = shards.asyncRecoverReplica( replica, - (indexShard, node) -> new RecoveryTarget(indexShard, node, null, null, recoveryListener) { + (indexShard, node) -> new RecoveryTarget(indexShard, node, 0L, null, null, recoveryListener) { @Override public void indexTranslogOperations( final List operations, @@ -784,7 +784,7 @@ public BlockingTarget( PeerRecoveryTargetService.RecoveryListener listener, Logger logger ) { - super(shard, sourceNode, null, null, listener); + super(shard, sourceNode, 0L, null, null, listener); this.recoveryBlocked = recoveryBlocked; this.releaseRecovery = releaseRecovery; this.stageToBlock = stageToBlock; diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index a3061df8839fb..5fdfb92d3a193 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -2856,31 +2856,37 @@ public void testTranslogRecoverySyncsTranslog() throws IOException { indexDoc(primary, "_doc", "0", "{\"foo\" : \"bar\"}"); IndexShard replica = newShard(primary.shardId(), false, "n2", metadata, null); - recoverReplica(replica, primary, (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, null, null, recoveryListener) { - @Override - public void indexTranslogOperations( - final List operations, - final int totalTranslogOps, - final long maxSeenAutoIdTimestamp, - final long maxSeqNoOfUpdatesOrDeletes, - final RetentionLeases retentionLeases, - final long mappingVersion, - final ActionListener listener - ) { - super.indexTranslogOperations( - operations, - totalTranslogOps, - maxSeenAutoIdTimestamp, - maxSeqNoOfUpdatesOrDeletes, - retentionLeases, - mappingVersion, - listener.delegateFailureAndWrap((l, r) -> { - assertFalse(replica.isSyncNeeded()); - l.onResponse(r); - }) - ); - } - }, true, true); + recoverReplica( + replica, + primary, + (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, 0L, null, null, recoveryListener) { + @Override + public void indexTranslogOperations( + final List operations, + final int totalTranslogOps, + final long maxSeenAutoIdTimestamp, + final long maxSeqNoOfUpdatesOrDeletes, + final RetentionLeases retentionLeases, + final long mappingVersion, + final ActionListener listener + ) { + super.indexTranslogOperations( + operations, + totalTranslogOps, + maxSeenAutoIdTimestamp, + maxSeqNoOfUpdatesOrDeletes, + retentionLeases, + mappingVersion, + listener.delegateFailureAndWrap((l, r) -> { + assertFalse(replica.isSyncNeeded()); + l.onResponse(r); + }) + ); + } + }, + true, + true + ); closeShards(primary, replica); } @@ -2980,32 +2986,38 @@ public void testShardActiveDuringPeerRecovery() throws IOException { replica.markAsRecovering("for testing", new RecoveryState(replica.routingEntry(), localNode, localNode)); // Shard is still inactive since we haven't started recovering yet assertFalse(replica.isActive()); - recoverReplica(replica, primary, (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, null, null, recoveryListener) { - @Override - public void indexTranslogOperations( - final List operations, - final int totalTranslogOps, - final long maxAutoIdTimestamp, - final long maxSeqNoOfUpdatesOrDeletes, - final RetentionLeases retentionLeases, - final long mappingVersion, - final ActionListener listener - ) { - super.indexTranslogOperations( - operations, - totalTranslogOps, - maxAutoIdTimestamp, - maxSeqNoOfUpdatesOrDeletes, - retentionLeases, - mappingVersion, - listener.delegateFailureAndWrap((l, checkpoint) -> { - l.onResponse(checkpoint); - // Shard should now be active since we did recover: - assertTrue(replica.isActive()); - }) - ); - } - }, false, true); + recoverReplica( + replica, + primary, + (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, 0L, null, null, recoveryListener) { + @Override + public void indexTranslogOperations( + final List operations, + final int totalTranslogOps, + final long maxAutoIdTimestamp, + final long maxSeqNoOfUpdatesOrDeletes, + final RetentionLeases retentionLeases, + final long mappingVersion, + final ActionListener listener + ) { + super.indexTranslogOperations( + operations, + totalTranslogOps, + maxAutoIdTimestamp, + maxSeqNoOfUpdatesOrDeletes, + retentionLeases, + mappingVersion, + listener.delegateFailureAndWrap((l, checkpoint) -> { + l.onResponse(checkpoint); + // Shard should now be active since we did recover: + assertTrue(replica.isActive()); + }) + ); + } + }, + false, + true + ); closeShards(primary, replica); } @@ -3033,48 +3045,54 @@ public void testRefreshListenersDuringPeerRecovery() throws IOException { DiscoveryNode localNode = DiscoveryNodeUtils.builder("foo").roles(emptySet()).build(); replica.markAsRecovering("for testing", new RecoveryState(replica.routingEntry(), localNode, localNode)); assertListenerCalled.accept(replica); - recoverReplica(replica, primary, (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, null, null, recoveryListener) { - // we're only checking that listeners are called when the engine is open, before there is no point - @Override - public void prepareForTranslogOperations(int totalTranslogOps, ActionListener listener) { - super.prepareForTranslogOperations(totalTranslogOps, listener.delegateFailureAndWrap((l, r) -> { - assertListenerCalled.accept(replica); - l.onResponse(r); - })); - } - - @Override - public void indexTranslogOperations( - final List operations, - final int totalTranslogOps, - final long maxAutoIdTimestamp, - final long maxSeqNoOfUpdatesOrDeletes, - final RetentionLeases retentionLeases, - final long mappingVersion, - final ActionListener listener - ) { - super.indexTranslogOperations( - operations, - totalTranslogOps, - maxAutoIdTimestamp, - maxSeqNoOfUpdatesOrDeletes, - retentionLeases, - mappingVersion, - listener.delegateFailureAndWrap((l, r) -> { + recoverReplica( + replica, + primary, + (shard, discoveryNode) -> new RecoveryTarget(shard, discoveryNode, 0L, null, null, recoveryListener) { + // we're only checking that listeners are called when the engine is open, before there is no point + @Override + public void prepareForTranslogOperations(int totalTranslogOps, ActionListener listener) { + super.prepareForTranslogOperations(totalTranslogOps, listener.delegateFailureAndWrap((l, r) -> { assertListenerCalled.accept(replica); l.onResponse(r); - }) - ); - } + })); + } - @Override - public void finalizeRecovery(long globalCheckpoint, long trimAboveSeqNo, ActionListener listener) { - super.finalizeRecovery(globalCheckpoint, trimAboveSeqNo, listener.delegateFailureAndWrap((l, r) -> { - assertListenerCalled.accept(replica); - l.onResponse(r); - })); - } - }, false, true); + @Override + public void indexTranslogOperations( + final List operations, + final int totalTranslogOps, + final long maxAutoIdTimestamp, + final long maxSeqNoOfUpdatesOrDeletes, + final RetentionLeases retentionLeases, + final long mappingVersion, + final ActionListener listener + ) { + super.indexTranslogOperations( + operations, + totalTranslogOps, + maxAutoIdTimestamp, + maxSeqNoOfUpdatesOrDeletes, + retentionLeases, + mappingVersion, + listener.delegateFailureAndWrap((l, r) -> { + assertListenerCalled.accept(replica); + l.onResponse(r); + }) + ); + } + + @Override + public void finalizeRecovery(long globalCheckpoint, long trimAboveSeqNo, ActionListener listener) { + super.finalizeRecovery(globalCheckpoint, trimAboveSeqNo, listener.delegateFailureAndWrap((l, r) -> { + assertListenerCalled.accept(replica); + l.onResponse(r); + })); + } + }, + false, + true + ); closeShards(primary, replica); } @@ -4740,7 +4758,7 @@ public void onRecoveryFailure(RecoveryFailedException e, boolean sendShardFailur assert false : "Unexpected failure"; } }; - recoverReplica(replicaShard, primary, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, null, null, recoveryListener) { + recoverReplica(replicaShard, primary, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, 0L, null, null, recoveryListener) { @Override public void indexTranslogOperations( List operations, diff --git a/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java b/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java index 9e1bcf10a8ab4..7d5098ab2a739 100644 --- a/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java +++ b/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java @@ -229,7 +229,7 @@ public MockIndexService indexService(Index index) { } @Override - public MockIndexShard createShard( + public void createShard( final ShardRouting shardRouting, final PeerRecoveryTargetService recoveryTargetService, final PeerRecoveryTargetService.RecoveryListener recoveryListener, @@ -238,21 +238,18 @@ public MockIndexShard createShard( final GlobalCheckpointSyncer globalCheckpointSyncer, final RetentionLeaseSyncer retentionLeaseSyncer, final DiscoveryNode targetNode, - final DiscoveryNode sourceNode + final DiscoveryNode sourceNode, + long clusterStateVersion ) throws IOException { failRandomly(); RecoveryState recoveryState = new RecoveryState(shardRouting, targetNode, sourceNode); MockIndexService indexService = indexService(recoveryState.getShardId().getIndex()); MockIndexShard indexShard = indexService.createShard(shardRouting); indexShard.recoveryState = recoveryState; - return indexShard; } @Override - public void processPendingDeletes(Index index, IndexSettings indexSettings, TimeValue timeValue) throws IOException, - InterruptedException { - - } + public void processPendingDeletes(Index index, IndexSettings indexSettings, TimeValue timeValue) {} private boolean hasIndex(Index index) { return indices.containsKey(index.getUUID()); diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoverySourceServiceTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoverySourceServiceTests.java index a18e7e8ce46f9..ecb0b4cf2d828 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoverySourceServiceTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoverySourceServiceTests.java @@ -43,6 +43,7 @@ public void testDuplicateRecoveries() throws IOException { PeerRecoverySourceService peerRecoverySourceService = new PeerRecoverySourceService( transportService, indicesService, + clusterService, new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), mock(RecoveryPlannerService.class) ); @@ -51,6 +52,7 @@ public void testDuplicateRecoveries() throws IOException { randomAlphaOfLength(10), getFakeDiscoNode("source"), getFakeDiscoNode("target"), + 0L, Store.MetadataSnapshot.EMPTY, randomBoolean(), randomLong(), diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java index 4305af1a1e3b4..16832aa07ccd6 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java @@ -100,7 +100,7 @@ public void testWriteFileChunksConcurrently() throws Exception { final DiscoveryNode pNode = getFakeDiscoNode(sourceShard.routingEntry().currentNodeId()); final DiscoveryNode rNode = getFakeDiscoNode(targetShard.routingEntry().currentNodeId()); targetShard.markAsRecovering("test-peer-recovery", new RecoveryState(targetShard.routingEntry(), rNode, pNode)); - final RecoveryTarget recoveryTarget = new RecoveryTarget(targetShard, null, null, null, null); + final RecoveryTarget recoveryTarget = new RecoveryTarget(targetShard, null, 0L, null, null, null); final PlainActionFuture receiveFileInfoFuture = new PlainActionFuture<>(); recoveryTarget.receiveFileInfo( mdFiles.stream().map(StoreFileMetadata::name).toList(), @@ -330,7 +330,7 @@ public void testResetStartingSeqNoIfLastCommitCorrupted() throws Exception { shard.prepareForIndexRecovery(); long startingSeqNo = shard.recoverLocallyUpToGlobalCheckpoint(); shard.store().markStoreCorrupted(new IOException("simulated")); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, null, null, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, null, null, null); StartRecoveryRequest request = PeerRecoveryTargetService.getStartRecoveryRequest(logger, rNode, recoveryTarget, startingSeqNo); assertThat(request.startingSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); assertThat(request.metadataSnapshot().size(), equalTo(0)); @@ -348,7 +348,7 @@ public void testMarkDoneFailureIsPropagated() throws Exception { shard.prepareForIndexRecovery(); PlainActionFuture future = PlainActionFuture.newFuture(); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, null, null, new PeerRecoveryTargetService.RecoveryListener() { + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, null, null, new PeerRecoveryTargetService.RecoveryListener() { @Override public void onRecoveryDone(RecoveryState state, ShardLongFieldRange timestampMillisFieldRange) { future.onResponse(null); @@ -388,7 +388,7 @@ public void testResetStartRequestIfTranslogIsCorrupted() throws Exception { shard = reinitShard(shard, ShardRoutingHelper.initWithSameId(shard.routingEntry(), RecoverySource.PeerRecoverySource.INSTANCE)); shard.markAsRecovering("peer recovery", new RecoveryState(shard.routingEntry(), pNode, rNode)); shard.prepareForIndexRecovery(); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, null, null, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, null, null, null); StartRecoveryRequest request = PeerRecoveryTargetService.getStartRecoveryRequest( logger, rNode, @@ -456,7 +456,7 @@ public int getReadSnapshotFileBufferSizeForRepo(String repository) { recoveryStateIndex.addFileDetail(storeFileMetadata.name(), storeFileMetadata.length(), false); recoveryStateIndex.setFileDetailsComplete(); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, snapshotFilesProvider, () -> {}, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, snapshotFilesProvider, () -> {}, null); PlainActionFuture writeSnapshotFileFuture = PlainActionFuture.newFuture(); recoveryTarget.restoreFileFromSnapshot(repositoryName, indexId, fileInfo, writeSnapshotFileFuture); @@ -528,7 +528,7 @@ public int getReadSnapshotFileBufferSizeForRepo(String repository) { recoveryStateIndex.addFileDetail(storeFileMetadata.name(), storeFileMetadata.length(), false); recoveryStateIndex.setFileDetailsComplete(); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, snapshotFilesProvider, () -> {}, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, snapshotFilesProvider, () -> {}, null); String repositoryName = "repo"; IndexId indexId = new IndexId("index", "uuid"); @@ -635,7 +635,7 @@ public int getReadSnapshotFileBufferSizeForRepo(String repository) { } }; - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, snapshotFilesProvider, () -> {}, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, snapshotFilesProvider, () -> {}, null); String[] fileNamesBeforeRecoveringSnapshotFiles = directory.listAll(); @@ -701,7 +701,7 @@ public int getReadSnapshotFileBufferSizeForRepo(String repository) { recoveryStateIndex.addFileDetail(storeFileMetadata.name(), storeFileMetadata.length(), false); recoveryStateIndex.setFileDetailsComplete(); - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, snapshotFilesProvider, () -> {}, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, snapshotFilesProvider, () -> {}, null); String repository = "repo"; IndexId indexId = new IndexId("index", "uuid"); @@ -749,7 +749,7 @@ public void testSnapshotFileDownloadPermitIsReleasedAfterClosingRecoveryTarget() Releasable snapshotFileDownloadsPermit = () -> { assertThat(snapshotFileDownloadsPermitFlag.compareAndSet(false, true), is(equalTo(true))); }; - RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, null, snapshotFileDownloadsPermit, null); + RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, 0L, null, snapshotFileDownloadsPermit, null); recoveryTarget.decRef(); diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index a46cd75dfd493..d2f94ff2d344a 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -233,7 +233,7 @@ public void writeFileChunk( IOUtils.close(reader, store, multiFileWriter, targetStore); } - public StartRecoveryRequest getStartRecoveryRequest() throws IOException { + public StartRecoveryRequest getStartRecoveryRequest() { Store.MetadataSnapshot metadataSnapshot = randomBoolean() ? Store.MetadataSnapshot.EMPTY : new Store.MetadataSnapshot( @@ -246,6 +246,7 @@ public StartRecoveryRequest getStartRecoveryRequest() throws IOException { null, DiscoveryNodeUtils.builder("b").roles(emptySet()).build(), DiscoveryNodeUtils.builder("b").roles(emptySet()).build(), + 0L, metadataSnapshot, randomBoolean(), randomNonNegativeLong(), diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryTests.java index ecb1e2ccde132..20e85c9c6fed8 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryTests.java @@ -311,7 +311,7 @@ public void testPeerRecoverySendSafeCommitInFileBased() throws Exception { } IndexShard replicaShard = newShard(primaryShard.shardId(), false); updateMappings(replicaShard, primaryShard.indexSettings().getIndexMetadata()); - recoverReplica(replicaShard, primaryShard, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, null, null, recoveryListener) { + recoverReplica(replicaShard, primaryShard, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, 0L, null, null, recoveryListener) { @Override public void prepareForTranslogOperations(int totalTranslogOps, ActionListener listener) { super.prepareForTranslogOperations(totalTranslogOps, listener); @@ -432,7 +432,7 @@ public long addDocument(Iterable doc) throws IOExcepti allowShardFailures(); IndexShard replica = group.addReplica(); expectThrows(Exception.class, () -> group.recoverReplica(replica, (shard, sourceNode) -> { - return new RecoveryTarget(shard, sourceNode, null, null, new PeerRecoveryTargetService.RecoveryListener() { + return new RecoveryTarget(shard, sourceNode, 0L, null, null, new PeerRecoveryTargetService.RecoveryListener() { @Override public void onRecoveryDone(RecoveryState state, ShardLongFieldRange timestampMillisFieldRange) { throw new AssertionError("recovery must fail"); diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java index cf98626f26e1e..47d3777573c4f 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java @@ -9,11 +9,10 @@ package org.elasticsearch.indices.recovery; import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.common.UUIDs; -import org.elasticsearch.common.io.stream.InputStreamStreamInput; -import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -22,8 +21,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.TransportVersionUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.util.Collections; import static java.util.Collections.emptySet; @@ -47,6 +44,7 @@ public void testSerialization() throws Exception { UUIDs.randomBase64UUID(), DiscoveryNodeUtils.builder("a").roles(emptySet()).version(targetNodeVersion, IndexVersion.ZERO, IndexVersion.current()).build(), DiscoveryNodeUtils.builder("b").roles(emptySet()).version(targetNodeVersion, IndexVersion.ZERO, IndexVersion.current()).build(), + randomNonNegativeLong(), metadataSnapshot, randomBoolean(), randomNonNegativeLong(), @@ -54,15 +52,12 @@ public void testSerialization() throws Exception { randomBoolean() ); - final ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(); - final OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer); - out.setTransportVersion(serializationVersion); - outRequest.writeTo(out); - - final ByteArrayInputStream inBuffer = new ByteArrayInputStream(outBuffer.toByteArray()); - InputStreamStreamInput in = new InputStreamStreamInput(inBuffer); - in.setTransportVersion(serializationVersion); - final StartRecoveryRequest inRequest = new StartRecoveryRequest(in); + final StartRecoveryRequest inRequest = copyWriteable( + outRequest, + writableRegistry(), + StartRecoveryRequest::new, + serializationVersion + ); assertThat(outRequest.shardId(), equalTo(inRequest.shardId())); assertThat(outRequest.targetAllocationId(), equalTo(inRequest.targetAllocationId())); @@ -72,6 +67,12 @@ public void testSerialization() throws Exception { assertThat(outRequest.isPrimaryRelocation(), equalTo(inRequest.isPrimaryRelocation())); assertThat(outRequest.recoveryId(), equalTo(inRequest.recoveryId())); assertThat(outRequest.startingSeqNo(), equalTo(inRequest.startingSeqNo())); + + if (serializationVersion.onOrAfter(TransportVersions.WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED)) { + assertEquals(outRequest.clusterStateVersion(), inRequest.clusterStateVersion()); + } else { + assertEquals(0L, inRequest.clusterStateVersion()); + } } public void testDescription() { @@ -79,13 +80,14 @@ public void testDescription() { assertEquals( "recovery of [index][0] to " + node.descriptionWithoutAttributes() - + " [recoveryId=1, targetAllocationId=allocationId, startingSeqNo=-2, " + + " [recoveryId=1, targetAllocationId=allocationId, clusterStateVersion=3, startingSeqNo=-2, " + "primaryRelocation=false, canDownloadSnapshotFiles=true]", new StartRecoveryRequest( new ShardId("index", "uuid", 0), "allocationId", null, node, + 3, Store.MetadataSnapshot.EMPTY, false, 1, diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationActionTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationActionTests.java index f591be2d3255a..eae982b083488 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationActionTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/StatelessPrimaryRelocationActionTests.java @@ -32,7 +32,8 @@ protected StatelessPrimaryRelocationAction.Request createTestInstance() { randomNonNegativeLong(), new ShardId(randomIdentifier(), UUIDs.randomBase64UUID(), randomIntBetween(0, 99)), newDiscoveryNode(), - UUIDs.randomBase64UUID() + UUIDs.randomBase64UUID(), + randomNonNegativeLong() ); } @@ -43,30 +44,41 @@ private static DiscoveryNode newDiscoveryNode() { @Override protected StatelessPrimaryRelocationAction.Request mutateInstance(StatelessPrimaryRelocationAction.Request instance) throws IOException { - return switch (between(1, 4)) { + return switch (between(1, 5)) { case 1 -> new StatelessPrimaryRelocationAction.Request( randomValueOtherThan(instance.recoveryId(), ESTestCase::randomNonNegativeLong), instance.shardId(), instance.targetNode(), - instance.targetAllocationId() + instance.targetAllocationId(), + instance.clusterStateVersion() ); case 2 -> new StatelessPrimaryRelocationAction.Request( instance.recoveryId(), ShardIdTests.mutate(instance.shardId()), instance.targetNode(), - instance.targetAllocationId() + instance.targetAllocationId(), + instance.clusterStateVersion() ); case 3 -> new StatelessPrimaryRelocationAction.Request( instance.recoveryId(), instance.shardId(), randomValueOtherThan(instance.targetNode(), StatelessPrimaryRelocationActionTests::newDiscoveryNode), - instance.targetAllocationId() + instance.targetAllocationId(), + instance.clusterStateVersion() ); case 4 -> new StatelessPrimaryRelocationAction.Request( instance.recoveryId(), instance.shardId(), instance.targetNode(), - randomValueOtherThan(instance.targetAllocationId(), UUIDs::randomBase64UUID) + randomValueOtherThan(instance.targetAllocationId(), UUIDs::randomBase64UUID), + instance.clusterStateVersion() + ); + case 5 -> new StatelessPrimaryRelocationAction.Request( + instance.recoveryId(), + instance.shardId(), + instance.targetNode(), + instance.targetAllocationId(), + randomValueOtherThan(instance.clusterStateVersion(), ESTestCase::randomNonNegativeLong) ); default -> throw new AssertionError("impossible"); }; diff --git a/server/src/test/java/org/elasticsearch/recovery/RecoveriesCollectionTests.java b/server/src/test/java/org/elasticsearch/recovery/RecoveriesCollectionTests.java index b97a841c00118..4896f118a1327 100644 --- a/server/src/test/java/org/elasticsearch/recovery/RecoveriesCollectionTests.java +++ b/server/src/test/java/org/elasticsearch/recovery/RecoveriesCollectionTests.java @@ -160,6 +160,6 @@ long startRecovery( final DiscoveryNode rNode = getDiscoveryNode(indexShard.routingEntry().currentNodeId()); indexShard.markAsRecovering("remote", new RecoveryState(indexShard.routingEntry(), sourceNode, rNode)); indexShard.prepareForIndexRecovery(); - return collection.startRecovery(indexShard, sourceNode, null, listener, timeValue, null); + return collection.startRecovery(indexShard, sourceNode, 0L, null, listener, timeValue, null); } } diff --git a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java index c1959ec85d54b..1b5ff3f39be22 100644 --- a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java +++ b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java @@ -1829,6 +1829,7 @@ protected void assertSnapshotOrGenericThread() { peerRecoverySourceService = new PeerRecoverySourceService( transportService, indicesService, + clusterService, recoverySettings, PeerOnlyRecoveryPlannerService.INSTANCE ); diff --git a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 9d607da6a3873..c2ce750f155cf 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -451,7 +451,7 @@ public synchronized boolean removeReplica(IndexShard replica) throws IOException } public void recoverReplica(IndexShard replica) throws IOException { - recoverReplica(replica, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, null, null, recoveryListener)); + recoverReplica(replica, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, 0L, null, null, recoveryListener)); } public void recoverReplica(IndexShard replica, BiFunction targetSupplier) diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 5a6d8bb878af8..f36951cec0b84 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -751,7 +751,7 @@ protected void recoverReplica(IndexShard replica, IndexShard primary, boolean st recoverReplica( replica, primary, - (r, sourceNode) -> new RecoveryTarget(r, sourceNode, null, null, recoveryListener), + (r, sourceNode) -> new RecoveryTarget(r, sourceNode, 0L, null, null, recoveryListener), true, startReplica ); 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 bdf3a1e8b5018..540ef4cf1027b 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -2035,4 +2035,8 @@ protected static boolean isTurkishLocale() { return Locale.getDefault().getLanguage().equals(new Locale("tr").getLanguage()) || Locale.getDefault().getLanguage().equals(new Locale("az").getLanguage()); } + + public static void fail(Throwable t, String msg, Object... args) { + throw new AssertionError(org.elasticsearch.common.Strings.format(msg, args), t); + } } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java index 78132749e2923..e37bd879ec943 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java @@ -433,7 +433,7 @@ protected EngineFactory getEngineFactory(ShardRouting routing) { // operations between the local checkpoint and max_seq_no which the recovering replica is waiting for. recoveryFuture = group.asyncRecoverReplica( newReplica, - (shard, sourceNode) -> new RecoveryTarget(shard, sourceNode, null, null, recoveryListener) { + (shard, sourceNode) -> new RecoveryTarget(shard, sourceNode, 0L, null, null, recoveryListener) { } ); } From 75b6f96453a08e421cc4b68207b1f591d092d73a Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 21 Sep 2023 09:37:11 +0200 Subject: [PATCH 007/155] Adjust ExpressionAggregationScript to support inter-segment concurrency (#99667) Handling of _value in a script agg does not support search concurrency when using the expression script engine. The reason is that the value gets set globally assuming sequential execution. This commit addresses that by setting the value to the values source associated with the correct leaf reader context, while it was previosly being set on a shared data structure. Closes #99156 --- .../script/expression/MoreExpressionIT.java | 1 - .../ExpressionAggregationScript.java | 2 +- .../ReplaceableConstDoubleValueSource.java | 23 ++++++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java index 02c17977daf6a..e7d6d127174ec 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java @@ -502,7 +502,6 @@ public void testSpecialValueVariable() throws Exception { assertThat(stats.getAvg(), equalTo(3.0)); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99156") public void testStringSpecialValueVariable() throws Exception { // i.e. expression script for term aggregations, which is not allowed assertAcked(indicesAdmin().prepareCreate("test").setMapping("text", "type=keyword").get()); diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionAggregationScript.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionAggregationScript.java index ec9435d9386b5..df08c0908e182 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionAggregationScript.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionAggregationScript.java @@ -83,7 +83,7 @@ public void setNextAggregationValue(Object value) { // _value isn't used in script if specialValue == null if (specialValue != null) { if (value instanceof Number) { - specialValue.setValue(((Number) value).doubleValue()); + specialValue.setValue(leaf, ((Number) value).doubleValue()); } else { throw new GeneralScriptException("Cannot use expression with text variable using " + exprScript); } diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java index 50a70fccdcd44..903ddaf72340e 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java @@ -15,20 +15,21 @@ import org.apache.lucene.search.IndexSearcher; import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A {@link DoubleValuesSource} which has a stub {@link DoubleValues} that holds a dynamically replaceable constant double. */ final class ReplaceableConstDoubleValueSource extends DoubleValuesSource { - final ReplaceableConstDoubleValues fv; - ReplaceableConstDoubleValueSource() { - fv = new ReplaceableConstDoubleValues(); - } + private final Map specialValues = new ConcurrentHashMap<>(); @Override public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { - return fv; + ReplaceableConstDoubleValues replaceableConstDoubleValues = new ReplaceableConstDoubleValues(); + specialValues.put(ctx, replaceableConstDoubleValues); + return replaceableConstDoubleValues; } @Override @@ -38,8 +39,12 @@ public boolean needsScores() { @Override public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException { - if (fv.advanceExact(docId)) return Explanation.match((float) fv.doubleValue(), "ReplaceableConstDoubleValues"); - else return Explanation.noMatch("ReplaceableConstDoubleValues"); + // TODO where is this explain called? I bet it's never tested, and probably never called. + ReplaceableConstDoubleValues fv = specialValues.get(ctx); + if (fv.advanceExact(docId)) { + return Explanation.match((float) fv.doubleValue(), "ReplaceableConstDoubleValues"); + } + return Explanation.noMatch("ReplaceableConstDoubleValues"); } @Override @@ -52,7 +57,9 @@ public int hashCode() { return System.identityHashCode(this); } - public void setValue(double v) { + public void setValue(LeafReaderContext ctx, double v) { + ReplaceableConstDoubleValues fv = specialValues.get(ctx); + assert fv != null : "getValues must be called before setValue for any given leaf reader context"; fv.setValue(v); } From f35f7bcabf3a6dabe46d08344d28db7242cb2ae3 Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Thu, 21 Sep 2023 09:11:15 +0100 Subject: [PATCH 008/155] [ML] Adjust a memory limit in AutodetectMemoryLimitIT (#99715) Account for slight increase in memory overhead due to changes to some Boost containers --- .../xpack/ml/integration/AutodetectMemoryLimitIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java index ada95c7e0034c..1fc807df55baa 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java @@ -225,7 +225,7 @@ public void testManyDistinctOverFields() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(117000000L)); + assertThat(modelSizeStats.getModelBytes(), lessThan(117500000L)); assertThat(modelSizeStats.getModelBytes(), greaterThan(90000000L)); assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } From a6abdc05cc09b64eaade386878e6ac61efc32803 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Thu, 21 Sep 2023 10:40:31 +0200 Subject: [PATCH 009/155] ESQL: Better management of not stored TEXT fiels with synthetic source (#99695) --- docs/changelog/99695.yaml | 5 +++++ .../org/elasticsearch/compute/lucene/ValueSources.java | 2 +- .../resources/rest-api-spec/test/80_text.yml | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 docs/changelog/99695.yaml diff --git a/docs/changelog/99695.yaml b/docs/changelog/99695.yaml new file mode 100644 index 0000000000000..6dc4037a57763 --- /dev/null +++ b/docs/changelog/99695.yaml @@ -0,0 +1,5 @@ +pr: 99695 +summary: "ESQL: Better management of not stored TEXT fiels with synthetic source" +area: ES|QL +type: bug +issues: [] diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java index d1d68df52362c..b7eb47a7a52d3 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java @@ -60,7 +60,7 @@ public static List sources( // MatchOnlyTextFieldMapper class lives in the mapper-extras module. We use string equality // for the field type name to avoid adding a dependency to the module if (fieldType instanceof KeywordFieldMapper.KeywordFieldType - || fieldType instanceof TextFieldMapper.TextFieldType + || fieldType instanceof TextFieldMapper.TextFieldType tft && (tft.isSyntheticSource() == false || tft.isStored()) || MATCH_ONLY_TEXT.equals(fieldType.typeName())) { ValuesSource vs = textValueSource(ctx, fieldType); sources.add(new ValueSourceInfo(CoreValuesSourceType.KEYWORD, vs, elementType, ctx.getIndexReader())); diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml index 4103bee7e290f..5f9ecbdf747bf 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml @@ -323,9 +323,7 @@ setup: --- "text with synthetic source": - skip: - version: "all" - reason: "AwaitsFix https://github.com/elastic/elasticsearch/issues/99183" - + features: allowed_warnings_regex - do: indices.create: index: test2 @@ -355,6 +353,8 @@ setup: - { "emp_no": 20, "name": "John", "job": "Payroll Specialist" } - do: + allowed_warnings_regex: + - "Field \\[job\\] cannot be retrieved, it is unsupported or not indexed; returning null" esql.query: body: query: 'from test2 | sort emp_no | keep job' @@ -363,8 +363,8 @@ setup: - match: { columns.0.type: "text" } - length: { values: 2 } - - match: { values.0.0: "IT Director" } - - match: { values.1.0: "Payroll Specialist" } + - match: { values.0.0: null } # awaiting a complete fix for https://github.com/elastic/elasticsearch/issues/99183 + - match: { values.1.0: null } # for now, since we cannot extract text values from synthetic source, we at least avoid to throw exceptions --- From 60940c7a9c401b169d92065d21ecc151e886bfb6 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Thu, 21 Sep 2023 05:06:29 -0400 Subject: [PATCH 010/155] [main] [DOCS] Add 8.10.2 release notes (#99722) --- docs/reference/release-notes.asciidoc | 2 ++ docs/reference/release-notes/8.10.2.asciidoc | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 docs/reference/release-notes/8.10.2.asciidoc diff --git a/docs/reference/release-notes.asciidoc b/docs/reference/release-notes.asciidoc index cb02320529457..aecbaf0fc928d 100644 --- a/docs/reference/release-notes.asciidoc +++ b/docs/reference/release-notes.asciidoc @@ -7,6 +7,7 @@ This section summarizes the changes in each release. * <> +* <> * <> * <> * <> @@ -51,6 +52,7 @@ This section summarizes the changes in each release. -- include::release-notes/8.11.0.asciidoc[] +include::release-notes/8.10.2.asciidoc[] include::release-notes/8.10.1.asciidoc[] include::release-notes/8.10.0.asciidoc[] include::release-notes/8.9.2.asciidoc[] diff --git a/docs/reference/release-notes/8.10.2.asciidoc b/docs/reference/release-notes/8.10.2.asciidoc new file mode 100644 index 0000000000000..248f07530b8a9 --- /dev/null +++ b/docs/reference/release-notes/8.10.2.asciidoc @@ -0,0 +1,6 @@ +[[release-notes-8.10.2]] +== {es} version 8.10.2 + +8.10.2 contains no significant changes. + +Also see <>. From 16744b8a948a9d312d77ce29c34ecc918bf04d6f Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 21 Sep 2023 10:42:02 +0100 Subject: [PATCH 011/155] Remove obsolete docs for G1GC check (#99729) We removed this bootstrap check in #85361 but didn't remove its docs. This commit removes the obsolete docs. --- docs/reference/setup/bootstrap-checks.asciidoc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/reference/setup/bootstrap-checks.asciidoc b/docs/reference/setup/bootstrap-checks.asciidoc index c4b6206c3b8cf..20f93496934f7 100644 --- a/docs/reference/setup/bootstrap-checks.asciidoc +++ b/docs/reference/setup/bootstrap-checks.asciidoc @@ -221,14 +221,6 @@ releases are not suitable for production. The early-access check detects these early-access snapshots. To pass this check, you must start Elasticsearch on a release build of the JVM. -=== G1GC check - -Early versions of the HotSpot JVM that shipped with JDK 8 are known to -have issues that can lead to index corruption when the G1GC collector is -enabled. The versions impacted are those earlier than the version of -HotSpot that shipped with JDK 8u40. The G1GC check detects these early -versions of the HotSpot JVM. - === All permission check The all permission check ensures that the security policy used during bootstrap From 1b8df61bd63d979fdd70ec8532f6bbf894634303 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 21 Sep 2023 11:16:37 +0100 Subject: [PATCH 012/155] Limit test parallelism to 1 for junit bwc tests (#99733) gradle runs test tasks in parallel, this results in multiple test clusters being created, which breaks CI. --- qa/full-cluster-restart/build.gradle | 5 +++++ qa/rolling-upgrade/build.gradle | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/qa/full-cluster-restart/build.gradle b/qa/full-cluster-restart/build.gradle index 06442b7cfe6a6..b6ecc054c9e41 100644 --- a/qa/full-cluster-restart/build.gradle +++ b/qa/full-cluster-restart/build.gradle @@ -13,6 +13,11 @@ apply plugin: 'elasticsearch.internal-java-rest-test' apply plugin: 'elasticsearch.internal-test-artifact' apply plugin: 'elasticsearch.bwc-test' +test { + // CI doesn't like it when there's multiple clusters running at once + maxParallelForks = 1 +} + BuildParams.bwcVersions.withIndexCompatible { bwcVersion, baseName -> tasks.register(bwcTaskName(bwcVersion), StandaloneRestIntegTestTask) { usesBwcDistribution(bwcVersion) diff --git a/qa/rolling-upgrade/build.gradle b/qa/rolling-upgrade/build.gradle index ea582ea7fc213..9abe03ab923b1 100644 --- a/qa/rolling-upgrade/build.gradle +++ b/qa/rolling-upgrade/build.gradle @@ -18,6 +18,11 @@ testArtifacts { registerTestArtifactFromSourceSet(sourceSets.javaRestTest) } +test { + // CI doesn't like it when there's multiple clusters running at once + maxParallelForks = 1 +} + BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> tasks.register(bwcTaskName(bwcVersion), StandaloneRestIntegTestTask) { usesBwcDistribution(bwcVersion) From d0e91c9bef145e5ceee9df66603baf4639d0cba4 Mon Sep 17 00:00:00 2001 From: Pooya Salehi Date: Thu, 21 Sep 2023 12:46:41 +0200 Subject: [PATCH 013/155] Bump TransportVersion and add RecoveryCommitTooNewException (#99591) Relates ES-6790 --- .../elasticsearch/ElasticsearchException.java | 9 ++++++- .../org/elasticsearch/TransportVersions.java | 1 + .../RecoveryCommitTooNewException.java | 26 +++++++++++++++++++ .../ExceptionSerializationTests.java | 2 ++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index a333f62593dc2..a84c9017859c8 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -28,6 +28,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.indices.recovery.RecoveryCommitTooNewException; import org.elasticsearch.rest.ApiNotAvailableException; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchException; @@ -1846,7 +1847,13 @@ private enum ElasticsearchExceptionHandle { 170, TransportVersions.V_8_500_020 ), - API_NOT_AVAILABLE_EXCEPTION(ApiNotAvailableException.class, ApiNotAvailableException::new, 171, TransportVersions.V_8_500_065); + API_NOT_AVAILABLE_EXCEPTION(ApiNotAvailableException.class, ApiNotAvailableException::new, 171, TransportVersions.V_8_500_065), + RECOVERY_COMMIT_TOO_NEW_EXCEPTION( + RecoveryCommitTooNewException.class, + RecoveryCommitTooNewException::new, + 172, + TransportVersions.RECOVERY_COMMIT_TOO_NEW_EXCEPTION_ADDED + ); final Class exceptionClass; final CheckedFunction constructor; diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 170f9140bae21..1fddeaf5d837e 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -143,6 +143,7 @@ static TransportVersion def(int id) { public static final TransportVersion FIRST_NEW_ID_LAYOUT = def(8_501_00_0); public static final TransportVersion COMMIT_PRIMARY_TERM_GENERATION = def(8_501_00_1); public static final TransportVersion WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED = def(8_502_00_0); + public static final TransportVersion RECOVERY_COMMIT_TOO_NEW_EXCEPTION_ADDED = def(8_503_00_0); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java new file mode 100644 index 0000000000000..732b627167701 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.indices.recovery; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.index.shard.ShardId; + +import java.io.IOException; + +public class RecoveryCommitTooNewException extends ElasticsearchException { + public RecoveryCommitTooNewException(ShardId shardId, String message) { + super(message); + setShard(shardId); + } + + public RecoveryCommitTooNewException(StreamInput in) throws IOException { + super(in); + } +} diff --git a/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java b/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java index 3c313ab5cd29f..45d3233e29ee0 100644 --- a/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java @@ -64,6 +64,7 @@ import org.elasticsearch.indices.InvalidIndexTemplateException; import org.elasticsearch.indices.recovery.PeerRecoveryNotFound; import org.elasticsearch.indices.recovery.RecoverFilesRecoveryException; +import org.elasticsearch.indices.recovery.RecoveryCommitTooNewException; import org.elasticsearch.ingest.IngestProcessorException; import org.elasticsearch.repositories.RepositoryConflictException; import org.elasticsearch.repositories.RepositoryException; @@ -832,6 +833,7 @@ public void testIds() { ids.put(169, HttpHeadersValidationException.class); ids.put(170, ElasticsearchRoleRestrictionException.class); ids.put(171, ApiNotAvailableException.class); + ids.put(172, RecoveryCommitTooNewException.class); Map, Integer> reverse = new HashMap<>(); for (Map.Entry> entry : ids.entrySet()) { From bf34036c8c6bf7a56c814bbfbb82e768547fdfba Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 21 Sep 2023 13:00:13 +0100 Subject: [PATCH 014/155] Discovery troubleshooting next steps (#99743) Adds a little more detail on how to react if you see evidence that the Elasticsearch process is pausing for a long time due to long GCs or VM pauses. --- .../troubleshooting/network-timeouts.asciidoc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/reference/troubleshooting/network-timeouts.asciidoc b/docs/reference/troubleshooting/network-timeouts.asciidoc index df961c83a292d..c15c5ee0d58a5 100644 --- a/docs/reference/troubleshooting/network-timeouts.asciidoc +++ b/docs/reference/troubleshooting/network-timeouts.asciidoc @@ -1,11 +1,17 @@ tag::troubleshooting-network-timeouts-gc-vm[] * GC pauses are recorded in the GC logs that {es} emits by default, and also usually by the `JvmMonitorService` in the main node logs. Use these logs to -confirm whether or not GC is resulting in delays. +confirm whether or not the node is experiencing high heap usage with long GC +pauses. If so, <> has some suggestions for further investigation but typically you +will need to capture a heap dump during a time of high heap usage to fully +understand the problem. * VM pauses also affect other processes on the same host. A VM pause also typically causes a discontinuity in the system clock, which {es} will report in -its logs. +its logs. If you see evidence of other processes pausing at the same time, or +unexpected clock discontinuities, investigate the infrastructure on which you +are running {es}. end::troubleshooting-network-timeouts-gc-vm[] tag::troubleshooting-network-timeouts-packet-capture-elections[] From ee133e0758362bd3f13428067c549e2ec220652d Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 21 Sep 2023 09:03:25 -0400 Subject: [PATCH 015/155] Bump versions after 8.10.2 release --- .buildkite/pipelines/intake.yml | 2 +- .buildkite/pipelines/periodic-packaging.yml | 16 ++++++++++++++++ .buildkite/pipelines/periodic.yml | 10 ++++++++++ .ci/bwcVersions | 1 + .ci/snapshotBwcVersions | 2 +- .../src/main/java/org/elasticsearch/Version.java | 1 + 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.buildkite/pipelines/intake.yml b/.buildkite/pipelines/intake.yml index 0f89ba9b8aeba..07cf55e0b3adf 100644 --- a/.buildkite/pipelines/intake.yml +++ b/.buildkite/pipelines/intake.yml @@ -40,7 +40,7 @@ steps: timeout_in_minutes: 300 matrix: setup: - BWC_VERSION: ["7.17.14", "8.10.2", "8.11.0"] + BWC_VERSION: ["7.17.14", "8.10.3", "8.11.0"] agents: provider: gcp image: family/elasticsearch-ubuntu-2004 diff --git a/.buildkite/pipelines/periodic-packaging.yml b/.buildkite/pipelines/periodic-packaging.yml index 1fd69ec8d8a2c..ce0746a5726cc 100644 --- a/.buildkite/pipelines/periodic-packaging.yml +++ b/.buildkite/pipelines/periodic-packaging.yml @@ -1632,6 +1632,22 @@ steps: env: BWC_VERSION: 8.10.2 + - label: "{{matrix.image}} / 8.10.3 / packaging-tests-upgrade" + command: ./.ci/scripts/packaging-test.sh --build-cache -Dorg.elasticsearch.build.cache.url=https://gradle-enterprise.elastic.co/cache/ -Dbwc.checkout.align=true destructiveDistroUpgradeTest.v8.10.3 + timeout_in_minutes: 300 + matrix: + setup: + image: + - rocky-8 + - ubuntu-2004 + agents: + provider: gcp + image: family/elasticsearch-{{matrix.image}} + machineType: custom-16-32768 + buildDirectory: /dev/shm/bk + env: + BWC_VERSION: 8.10.3 + - label: "{{matrix.image}} / 8.11.0 / packaging-tests-upgrade" command: ./.ci/scripts/packaging-test.sh --build-cache -Dorg.elasticsearch.build.cache.url=https://gradle-enterprise.elastic.co/cache/ -Dbwc.checkout.align=true destructiveDistroUpgradeTest.v8.11.0 timeout_in_minutes: 300 diff --git a/.buildkite/pipelines/periodic.yml b/.buildkite/pipelines/periodic.yml index fe28b0a0cea64..967dcbb8cf535 100644 --- a/.buildkite/pipelines/periodic.yml +++ b/.buildkite/pipelines/periodic.yml @@ -1002,6 +1002,16 @@ steps: buildDirectory: /dev/shm/bk env: BWC_VERSION: 8.10.2 + - label: 8.10.3 / bwc + command: .ci/scripts/run-gradle.sh -Dbwc.checkout.align=true v8.10.3#bwcTest + timeout_in_minutes: 300 + agents: + provider: gcp + image: family/elasticsearch-ubuntu-2004 + machineType: custom-32-98304 + buildDirectory: /dev/shm/bk + env: + BWC_VERSION: 8.10.3 - label: 8.11.0 / bwc command: .ci/scripts/run-gradle.sh -Dbwc.checkout.align=true v8.11.0#bwcTest timeout_in_minutes: 300 diff --git a/.ci/bwcVersions b/.ci/bwcVersions index ea42d7d4f0cc6..90ba9b8131dcd 100644 --- a/.ci/bwcVersions +++ b/.ci/bwcVersions @@ -99,4 +99,5 @@ BWC_VERSION: - "8.10.0" - "8.10.1" - "8.10.2" + - "8.10.3" - "8.11.0" diff --git a/.ci/snapshotBwcVersions b/.ci/snapshotBwcVersions index 2620f98f1cec1..1bc1fa321d9da 100644 --- a/.ci/snapshotBwcVersions +++ b/.ci/snapshotBwcVersions @@ -1,4 +1,4 @@ BWC_VERSION: - "7.17.14" - - "8.10.2" + - "8.10.3" - "8.11.0" diff --git a/server/src/main/java/org/elasticsearch/Version.java b/server/src/main/java/org/elasticsearch/Version.java index 43b4d2c8bb37a..591f93a34726f 100644 --- a/server/src/main/java/org/elasticsearch/Version.java +++ b/server/src/main/java/org/elasticsearch/Version.java @@ -150,6 +150,7 @@ public class Version implements VersionId, ToXContentFragment { public static final Version V_8_10_0 = new Version(8_10_00_99); public static final Version V_8_10_1 = new Version(8_10_01_99); public static final Version V_8_10_2 = new Version(8_10_02_99); + public static final Version V_8_10_3 = new Version(8_10_03_99); public static final Version V_8_11_0 = new Version(8_11_00_99); public static final Version CURRENT = V_8_11_0; From 010b11afc0d347f920c0492924eb6e0f0e1d08c0 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 21 Sep 2023 15:09:03 +0200 Subject: [PATCH 016/155] [ES|QL] Improve error message in case of unsupported time interval (#99662) * Improve error message in case of unsupported time interval * From feedback: improve message Co-authored-by: Bogdan Pintea * From feedback: update test * Typo --------- Co-authored-by: Bogdan Pintea --- .../org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java | 2 +- .../org/elasticsearch/xpack/esql/parser/ExpressionTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java index bbffa6c63e9b1..aa653d36d141b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java @@ -233,7 +233,7 @@ public Object visitQualifiedIntegerLiteral(EsqlBaseParser.QualifiedIntegerLitera case "month", "months" -> Period.ofMonths(safeToInt(safeToLong(value))); case "year", "years" -> Period.ofYears(safeToInt(safeToLong(value))); - default -> throw new ParsingException(source, "Unexpected numeric qualifier '{}'", qualifier); + default -> throw new ParsingException(source, "Unexpected time interval qualifier: '{}'", qualifier); }; return new Literal(source, quantity, quantity instanceof Duration ? TIME_DURATION : DATE_PERIOD); } catch (QlIllegalArgumentException | ArithmeticException e) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/ExpressionTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/ExpressionTests.java index ae5de9a0a13e2..d3067ba7f306f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/ExpressionTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/ExpressionTests.java @@ -410,7 +410,7 @@ public void testDatePeriodLiterals() { } public void testUnknownNumericQualifier() { - assertParsingException(() -> whereExpression("1 decade"), "Unexpected numeric qualifier 'decade'"); + assertParsingException(() -> whereExpression("1 decade"), "Unexpected time interval qualifier: 'decade'"); } public void testQualifiedDecimalLiteral() { From 61abe140c79bb69884f4801291e393e3b501d07d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 21 Sep 2023 06:37:26 -0700 Subject: [PATCH 017/155] Upgrade bundled JDK to Java 21 (#99724) Java 21 is now GA making Java 20 EOL. --- build-tools-internal/version.properties | 2 +- docs/changelog/99724.yaml | 5 +++++ gradle/verification-metadata.xml | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/99724.yaml diff --git a/build-tools-internal/version.properties b/build-tools-internal/version.properties index 274f6a98dc464..aebdabee7aed7 100644 --- a/build-tools-internal/version.properties +++ b/build-tools-internal/version.properties @@ -2,7 +2,7 @@ elasticsearch = 8.11.0 lucene = 9.7.0 bundled_jdk_vendor = openjdk -bundled_jdk = 20.0.2+9@6e380f22cbe7469fa75fb448bd903d8e +bundled_jdk = 21+35@fd2272bbf8e04c3dbaee13770090416c # optional dependencies spatial4j = 0.7 diff --git a/docs/changelog/99724.yaml b/docs/changelog/99724.yaml new file mode 100644 index 0000000000000..4fe78687bf72b --- /dev/null +++ b/docs/changelog/99724.yaml @@ -0,0 +1,5 @@ +pr: 99724 +summary: Upgrade bundled JDK to Java 21 +area: Packaging +type: upgrade +issues: [] diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6cd8d56be9530..aec85d9ed5d6b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1600,6 +1600,27 @@ + + + + + + + + + + + + + + + + + + + + + From 06f09d861de538c6989186187b2fa8d96e5e12fe Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 21 Sep 2023 14:42:04 +0100 Subject: [PATCH 018/155] Revert "Migrate rolling upgrade tests to new junit format" (#99750) Reverts elastic/elasticsearch#99572 and #99733 The new tests are unstable, and don't work on CI. This re-opens https://github.com/elastic/elasticsearch/issues/97200 --- .../internal/RestrictedBuildApiService.java | 1 - .../resources/checkstyle_suppressions.xml | 2 +- qa/full-cluster-restart/build.gradle | 5 - qa/rolling-upgrade-legacy/build.gradle | 143 ------- qa/rolling-upgrade/build.gradle | 137 ++++++- .../ParameterizedRollingUpgradeTestCase.java | 226 ----------- .../UpgradeWithOldIndexSettingsIT.java | 132 ------- .../upgrades/AbstractRollingTestCase.java | 18 + .../upgrades/DesiredNodesUpgradeIT.java | 66 ++-- .../upgrades/FeatureUpgradeIT.java | 16 +- .../elasticsearch/upgrades/FieldCapsIT.java | 34 +- .../elasticsearch/upgrades/IndexingIT.java | 363 ++++++++++-------- .../elasticsearch/upgrades/RecoveryIT.java | 20 +- .../upgrades/SnapshotBasedRecoveryIT.java | 160 ++++---- .../upgrades/SystemIndicesUpgradeIT.java | 12 +- .../org/elasticsearch/upgrades/TsdbIT.java | 46 +-- .../UpgradeClusterClientYamlTestSuiteIT.java | 5 + .../UpgradeWithOldIndexSettingsIT.java | 131 +++++++ .../org/elasticsearch/upgrades/XPackIT.java | 14 +- .../test/mixed_cluster/10_basic.yml | 0 .../mixed_cluster/20_camel_case_on_format.yml | 0 .../test/mixed_cluster/30_vector_search.yml | 0 .../test/old_cluster/10_basic.yml | 0 .../old_cluster/20_camel_case_on_format.yml | 0 .../test/old_cluster/30_vector_search.yml | 2 + .../test/upgraded_cluster/10_basic.yml | 0 .../20_camel_case_on_format.yml | 0 .../upgraded_cluster/30_vector_search.yml | 0 28 files changed, 636 insertions(+), 897 deletions(-) delete mode 100644 qa/rolling-upgrade-legacy/build.gradle delete mode 100644 qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java delete mode 100644 qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java (79%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java (82%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java (90%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/FieldCapsIT.java (94%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/IndexingIT.java (52%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java (97%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java (61%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java (95%) rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/TsdbIT.java (90%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java (95%) create mode 100644 qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java rename qa/rolling-upgrade/src/{javaRestTest => test}/java/org/elasticsearch/upgrades/XPackIT.java (93%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml (99%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade-legacy => rolling-upgrade}/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml (100%) diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java index 088f8290e713f..e9438eabadbb6 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java @@ -67,7 +67,6 @@ private static ListMultimap, String> createLegacyRestTestBasePluginUsag map.put(LegacyRestTestBasePlugin.class, ":qa:remote-clusters"); map.put(LegacyRestTestBasePlugin.class, ":qa:repository-multi-version"); map.put(LegacyRestTestBasePlugin.class, ":qa:rolling-upgrade"); - map.put(LegacyRestTestBasePlugin.class, ":qa:rolling-upgrade-legacy"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-http"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-ingest-disabled"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-ingest-with-all-dependencies"); diff --git a/build-tools-internal/src/main/resources/checkstyle_suppressions.xml b/build-tools-internal/src/main/resources/checkstyle_suppressions.xml index 9f074513b6d4e..211faf973b772 100644 --- a/build-tools-internal/src/main/resources/checkstyle_suppressions.xml +++ b/build-tools-internal/src/main/resources/checkstyle_suppressions.xml @@ -32,7 +32,7 @@ - + diff --git a/qa/full-cluster-restart/build.gradle b/qa/full-cluster-restart/build.gradle index b6ecc054c9e41..06442b7cfe6a6 100644 --- a/qa/full-cluster-restart/build.gradle +++ b/qa/full-cluster-restart/build.gradle @@ -13,11 +13,6 @@ apply plugin: 'elasticsearch.internal-java-rest-test' apply plugin: 'elasticsearch.internal-test-artifact' apply plugin: 'elasticsearch.bwc-test' -test { - // CI doesn't like it when there's multiple clusters running at once - maxParallelForks = 1 -} - BuildParams.bwcVersions.withIndexCompatible { bwcVersion, baseName -> tasks.register(bwcTaskName(bwcVersion), StandaloneRestIntegTestTask) { usesBwcDistribution(bwcVersion) diff --git a/qa/rolling-upgrade-legacy/build.gradle b/qa/rolling-upgrade-legacy/build.gradle deleted file mode 100644 index 7aca34bef8a1b..0000000000000 --- a/qa/rolling-upgrade-legacy/build.gradle +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - -import org.elasticsearch.gradle.Version -import org.elasticsearch.gradle.internal.BwcVersions -import org.elasticsearch.gradle.internal.info.BuildParams -import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask - -apply plugin: 'elasticsearch.internal-testclusters' -apply plugin: 'elasticsearch.standalone-rest-test' -apply plugin: 'elasticsearch.bwc-test' -apply plugin: 'elasticsearch.rest-resources' - -BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> - /* - * NOTE: This module is for the tests that were problematic when converting :qa:rolling-upgrade to the junit-based bwc test definition - * Over time, these should be migrated into the :qa:rolling-upgrade module and fixed properly - * - * The goal here is to: - *
    - *
  • start three nodes on the old version - *
  • run tests with systemProperty 'tests.rest.suite', 'old_cluster' - *
  • upgrade one node - *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' - *
  • upgrade one more node - *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' again - *
  • updgrade the last node - *
  • run tests with systemProperty 'tests.rest.suite', 'upgraded_cluster' - *
- */ - - def baseCluster = testClusters.register(baseName) { - versions = [bwcVersion.toString(), project.version] - numberOfNodes = 3 - - setting 'repositories.url.allowed_urls', 'http://snapshot.test*' - setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" - setting 'xpack.security.enabled', 'false' - setting 'logger.org.elasticsearch.cluster.service.MasterService', 'TRACE' - setting 'logger.org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator', 'TRACE' - setting 'logger.org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders', 'TRACE' - requiresFeature 'es.index_mode_feature_flag_registered', Version.fromString("8.0.0") - } - - String oldVersion = bwcVersion.toString() - - tasks.register("${baseName}#oldClusterTest", StandaloneRestIntegTestTask) { - dependsOn "processTestResources" - useCluster baseCluster - mustRunAfter("precommit") - doFirst { - delete("${buildDir}/cluster/shared/repo/${baseName}") - } - def excludeList = [] - systemProperty 'tests.rest.suite', 'old_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["old_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["old_cluster/30_vector_search/Create indexed byte vectors and search"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - - tasks.register("${baseName}#oneThirdUpgradedTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#oldClusterTest" - useCluster baseCluster - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - systemProperty 'tests.first_round', 'true' - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - - tasks.register("${baseName}#twoThirdsUpgradedTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#oneThirdUpgradedTest" - useCluster baseCluster - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - systemProperty 'tests.first_round', 'false' - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - - tasks.register("${baseName}#upgradedClusterTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#twoThirdsUpgradedTest" - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - useCluster testClusters.named(baseName) - systemProperty 'tests.rest.suite', 'upgraded_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["upgraded_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["upgraded_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - - tasks.register(bwcTaskName(bwcVersion)) { - dependsOn tasks.named("${baseName}#upgradedClusterTest") - } -} diff --git a/qa/rolling-upgrade/build.gradle b/qa/rolling-upgrade/build.gradle index 9abe03ab923b1..d3078dd8c9381 100644 --- a/qa/rolling-upgrade/build.gradle +++ b/qa/rolling-upgrade/build.gradle @@ -6,30 +6,135 @@ * Side Public License, v 1. */ + +import org.elasticsearch.gradle.Version +import org.elasticsearch.gradle.internal.BwcVersions import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask apply plugin: 'elasticsearch.internal-testclusters' -apply plugin: 'elasticsearch.internal-java-rest-test' -apply plugin: 'elasticsearch.internal-test-artifact-base' +apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.bwc-test' +apply plugin: 'elasticsearch.rest-resources' -testArtifacts { - registerTestArtifactFromSourceSet(sourceSets.javaRestTest) -} +BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> + /* + * The goal here is to: + *
    + *
  • start three nodes on the old version + *
  • run tests with systemProperty 'tests.rest.suite', 'old_cluster' + *
  • upgrade one node + *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' + *
  • upgrade one more node + *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' again + *
  • updgrade the last node + *
  • run tests with systemProperty 'tests.rest.suite', 'upgraded_cluster' + *
+ */ -test { - // CI doesn't like it when there's multiple clusters running at once - maxParallelForks = 1 -} + def baseCluster = testClusters.register(baseName) { + versions = [bwcVersion.toString(), project.version] + numberOfNodes = 3 -BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> - tasks.register(bwcTaskName(bwcVersion), StandaloneRestIntegTestTask) { - usesBwcDistribution(bwcVersion) - systemProperty("tests.old_cluster_version", bwcVersion) + setting 'repositories.url.allowed_urls', 'http://snapshot.test*' + setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" + setting 'xpack.security.enabled', 'false' + setting 'logger.org.elasticsearch.cluster.service.MasterService', 'TRACE' + setting 'logger.org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator', 'TRACE' + setting 'logger.org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders', 'TRACE' + requiresFeature 'es.index_mode_feature_flag_registered', Version.fromString("8.0.0") + } + + String oldVersion = bwcVersion.toString() + + tasks.register("${baseName}#oldClusterTest", StandaloneRestIntegTestTask) { + dependsOn "processTestResources" + useCluster baseCluster + mustRunAfter("precommit") + doFirst { + delete("${buildDir}/cluster/shared/repo/${baseName}") + } + def excludeList = [] + systemProperty 'tests.rest.suite', 'old_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["old_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["old_cluster/30_vector_search/Create indexed byte vectors and search"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#oneThirdUpgradedTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#oldClusterTest" + useCluster baseCluster + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + systemProperty 'tests.first_round', 'true' + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#twoThirdsUpgradedTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#oneThirdUpgradedTest" + useCluster baseCluster + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + systemProperty 'tests.first_round', 'false' + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#upgradedClusterTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#twoThirdsUpgradedTest" + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + useCluster testClusters.named(baseName) + systemProperty 'tests.rest.suite', 'upgraded_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["upgraded_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["upgraded_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } } -} -testClusters.configureEach { - setting 'xpack.security.enabled', 'false' + tasks.register(bwcTaskName(bwcVersion)) { + dependsOn tasks.named("${baseName}#upgradedClusterTest") + } } diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java deleted file mode 100644 index e1500358327c5..0000000000000 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.upgrades; - -import com.carrotsearch.randomizedtesting.annotations.Name; -import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; - -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.index.IndexVersion; -import org.elasticsearch.test.cluster.ElasticsearchCluster; -import org.elasticsearch.test.cluster.FeatureFlag; -import org.elasticsearch.test.cluster.local.distribution.DistributionType; -import org.elasticsearch.test.cluster.util.Version; -import org.elasticsearch.test.rest.ESRestTestCase; -import org.elasticsearch.test.rest.ObjectPath; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.rules.RuleChain; -import org.junit.rules.TemporaryFolder; -import org.junit.rules.TestRule; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; - -public abstract class ParameterizedRollingUpgradeTestCase extends ESRestTestCase { - private static final Version OLD_CLUSTER_VERSION = Version.fromString(System.getProperty("tests.old_cluster_version")); - - private static final TemporaryFolder repoDirectory = new TemporaryFolder(); - - private static final int NODE_NUM = 3; - - private static final ElasticsearchCluster cluster = ElasticsearchCluster.local() - .distribution(DistributionType.DEFAULT) - .version(getOldClusterTestVersion()) - .nodes(NODE_NUM) - .setting("path.repo", new Supplier<>() { - @Override - @SuppressForbidden(reason = "TemporaryFolder only has io.File methods, not nio.File") - public String get() { - return repoDirectory.getRoot().getPath(); - } - }) - .setting("xpack.security.enabled", "false") - .feature(FeatureFlag.TIME_SERIES_MODE) - .build(); - - @ClassRule - public static TestRule ruleChain = RuleChain.outerRule(repoDirectory).around(cluster); - - @ParametersFactory(shuffle = false) - public static Iterable parameters() { - return Stream.concat(Stream.of((Integer) null), IntStream.range(0, NODE_NUM).boxed()).map(n -> new Object[] { n }).toList(); - } - - private static final Set upgradedNodes = new HashSet<>(); - private static boolean upgradeFailed = false; - private static IndexVersion oldIndexVersion; - - private final Integer requestedUpgradeNode; - - protected ParameterizedRollingUpgradeTestCase(@Name("upgradeNode") Integer upgradeNode) { - this.requestedUpgradeNode = upgradeNode; - } - - @Before - public void extractOldIndexVersion() throws Exception { - if (oldIndexVersion == null && upgradedNodes.isEmpty()) { - IndexVersion indexVersion = null; // these should all be the same version - - Request request = new Request("GET", "_nodes"); - request.addParameter("filter_path", "nodes.*.index_version,nodes.*.name"); - Response response = client().performRequest(request); - ObjectPath objectPath = ObjectPath.createFromResponse(response); - Map nodeMap = objectPath.evaluate("nodes"); - for (String id : nodeMap.keySet()) { - Number ix = objectPath.evaluate("nodes." + id + ".index_version"); - IndexVersion version; - if (ix != null) { - version = IndexVersion.fromId(ix.intValue()); - } else { - // it doesn't have index version (pre 8.11) - just infer it from the release version - version = IndexVersion.fromId(getOldClusterVersion().id); - } - - if (indexVersion == null) { - indexVersion = version; - } else { - String name = objectPath.evaluate("nodes." + id + ".name"); - assertThat("Node " + name + " has a different index version to other nodes", version, equalTo(indexVersion)); - } - } - - assertThat("Index version could not be read", indexVersion, notNullValue()); - oldIndexVersion = indexVersion; - } - } - - @Before - public void upgradeNode() throws Exception { - // Skip remaining tests if upgrade failed - assumeFalse("Cluster upgrade failed", upgradeFailed); - - if (requestedUpgradeNode != null && upgradedNodes.contains(requestedUpgradeNode) == false) { - closeClients(); - // we might be running a specific upgrade test by itself - check previous nodes too - for (int n = 0; n <= requestedUpgradeNode; n++) { - if (upgradedNodes.add(n)) { - try { - cluster.upgradeNodeToVersion(n, Version.CURRENT); - } catch (Exception e) { - upgradeFailed = true; - throw e; - } - } - } - initClient(); - } - } - - @AfterClass - public static void resetNodes() { - oldIndexVersion = null; - upgradedNodes.clear(); - upgradeFailed = false; - } - - protected static org.elasticsearch.Version getOldClusterVersion() { - return org.elasticsearch.Version.fromString(OLD_CLUSTER_VERSION.toString()); - } - - protected static IndexVersion getOldClusterIndexVersion() { - assert oldIndexVersion != null; - return oldIndexVersion; - } - - protected static Version getOldClusterTestVersion() { - return Version.fromString(OLD_CLUSTER_VERSION.toString()); - } - - protected static boolean isOldCluster() { - return upgradedNodes.isEmpty(); - } - - protected static boolean isFirstMixedCluster() { - return upgradedNodes.size() == 1; - } - - protected static boolean isMixedCluster() { - return upgradedNodes.isEmpty() == false && upgradedNodes.size() < NODE_NUM; - } - - protected static boolean isUpgradedCluster() { - return upgradedNodes.size() == NODE_NUM; - } - - @Override - protected String getTestRestCluster() { - return cluster.getHttpAddresses(); - } - - @Override - protected final boolean resetFeatureStates() { - return false; - } - - @Override - protected final boolean preserveIndicesUponCompletion() { - return true; - } - - @Override - protected final boolean preserveDataStreamsUponCompletion() { - return true; - } - - @Override - protected final boolean preserveReposUponCompletion() { - return true; - } - - @Override - protected boolean preserveTemplatesUponCompletion() { - return true; - } - - @Override - protected boolean preserveClusterUponCompletion() { - return true; - } - - @Override - protected final Settings restClientSettings() { - return Settings.builder() - .put(super.restClientSettings()) - // increase the timeout here to 90 seconds to handle long waits for a green - // cluster health. the waits for green need to be longer than a minute to - // account for delayed shards - .put(ESRestTestCase.CLIENT_SOCKET_TIMEOUT, "90s") - .build(); - } - - @Override - protected final String getEnsureGreenTimeout() { - // increase the timeout here to 70 seconds to handle long waits for a green - // cluster health. the waits for green need to be longer than a minute to - // account for delayed shards - return "70s"; - } -} diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java deleted file mode 100644 index 3c6e15a78fc1f..0000000000000 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.upgrades; - -import com.carrotsearch.randomizedtesting.annotations.Name; - -import org.elasticsearch.Version; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.ResponseException; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.support.XContentMapValues; -import org.elasticsearch.core.Strings; - -import java.io.IOException; -import java.util.Map; - -import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; -import static org.hamcrest.Matchers.is; - -public class UpgradeWithOldIndexSettingsIT extends ParameterizedRollingUpgradeTestCase { - - public UpgradeWithOldIndexSettingsIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } - - private static final String INDEX_NAME = "test_index_old_settings"; - private static final String EXPECTED_WARNING = "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will " - + "be removed in a future release! See the breaking changes documentation for the next major version."; - - private static final String EXPECTED_V8_WARNING = "[index.indexing.slowlog.level] setting was deprecated in the previous Elasticsearch" - + " release and is removed in this release."; - - public void testOldIndexSettings() throws Exception { - if (isOldCluster()) { - Request createTestIndex = new Request("PUT", "/" + INDEX_NAME); - createTestIndex.setJsonEntity("{\"settings\": {\"index.indexing.slowlog.level\": \"WARN\"}}"); - createTestIndex.setOptions(expectWarnings(EXPECTED_WARNING)); - if (getOldClusterVersion().before(Version.V_8_0_0)) { - // create index with settings no longer valid in 8.0 - client().performRequest(createTestIndex); - } else { - assertTrue( - expectThrows(ResponseException.class, () -> client().performRequest(createTestIndex)).getMessage() - .contains("unknown setting [index.indexing.slowlog.level]") - ); - - Request createTestIndex1 = new Request("PUT", "/" + INDEX_NAME); - client().performRequest(createTestIndex1); - } - - // add some data - Request bulk = new Request("POST", "/_bulk"); - bulk.addParameter("refresh", "true"); - if (getOldClusterVersion().before(Version.V_8_0_0)) { - bulk.setOptions(expectWarnings(EXPECTED_WARNING)); - } - bulk.setJsonEntity(Strings.format(""" - {"index": {"_index": "%s"}} - {"f1": "v1", "f2": "v2"} - """, INDEX_NAME)); - client().performRequest(bulk); - } else if (isMixedCluster()) { - // add some more data - Request bulk = new Request("POST", "/_bulk"); - bulk.addParameter("refresh", "true"); - if (getOldClusterVersion().before(Version.V_8_0_0)) { - bulk.setOptions(expectWarnings(EXPECTED_WARNING)); - } - bulk.setJsonEntity(Strings.format(""" - {"index": {"_index": "%s"}} - {"f1": "v3", "f2": "v4"} - """, INDEX_NAME)); - client().performRequest(bulk); - } else { - if (getOldClusterVersion().before(Version.V_8_0_0)) { - Request createTestIndex = new Request("PUT", "/" + INDEX_NAME + "/_settings"); - // update index settings should work - createTestIndex.setJsonEntity("{\"index.indexing.slowlog.level\": \"INFO\"}"); - createTestIndex.setOptions(expectWarnings(EXPECTED_V8_WARNING)); - client().performRequest(createTestIndex); - - // ensure we were able to change the setting, despite it having no effect - Request indexSettingsRequest = new Request("GET", "/" + INDEX_NAME + "/_settings"); - Map response = entityAsMap(client().performRequest(indexSettingsRequest)); - - var slowLogLevel = (String) (XContentMapValues.extractValue( - INDEX_NAME + ".settings.index.indexing.slowlog.level", - response - )); - - // check that we can read our old index settings - assertThat(slowLogLevel, is("INFO")); - } - assertCount(INDEX_NAME, 2); - } - } - - private void assertCount(String index, int countAtLeast) throws IOException { - Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); - searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); - searchTestIndexRequest.addParameter("filter_path", "hits.total"); - Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); - Map response = entityAsMap(searchTestIndexResponse); - - var hitsTotal = (Integer) (XContentMapValues.extractValue("hits.total", response)); - - assertTrue(hitsTotal >= countAtLeast); - } - - public static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { - Request request = new Request("PUT", "/" + index + "/_settings"); - request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); - if (getOldClusterVersion().before(Version.V_7_17_9)) { - // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) - // Below warnings are set (and leaking) from an index in this test case - request.setOptions(expectVersionSpecificWarnings(v -> { - v.compatible( - "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " - + "See the breaking changes documentation for the next major version." - ); - })); - } - client().performRequest(request); - } -} diff --git a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java similarity index 79% rename from qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java index 74a8eb7fd1988..008a718be5873 100644 --- a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java @@ -9,8 +9,11 @@ import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexVersion; import org.elasticsearch.test.rest.ESRestTestCase; +import static org.hamcrest.Matchers.lessThan; + public abstract class AbstractRollingTestCase extends ESRestTestCase { protected enum ClusterType { OLD, @@ -31,6 +34,16 @@ public static ClusterType parse(String value) { protected static final boolean FIRST_MIXED_ROUND = Boolean.parseBoolean(System.getProperty("tests.first_round", "false")); protected static final Version UPGRADE_FROM_VERSION = Version.fromString(System.getProperty("tests.upgrade_from_version")); + protected static IndexVersion getOldClusterIndexVersion() { + var version = UPGRADE_FROM_VERSION; + if (version.equals(org.elasticsearch.Version.CURRENT)) { + return IndexVersion.current(); + } else { + assertThat("Index version needs to be added to rolling test parameters", version, lessThan(org.elasticsearch.Version.V_8_11_0)); + return IndexVersion.fromId(version.id); + } + } + @Override protected final boolean resetFeatureStates() { return false; @@ -41,6 +54,11 @@ protected final boolean preserveIndicesUponCompletion() { return true; } + @Override + protected final boolean preserveDataStreamsUponCompletion() { + return true; + } + @Override protected final boolean preserveReposUponCompletion() { return true; diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java similarity index 82% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java index e945d457986d0..5bafccf7aee1b 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.desirednodes.UpdateDesiredNodesRequest; import org.elasticsearch.client.Request; @@ -27,33 +25,24 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import static org.elasticsearch.node.Node.NODE_NAME_SETTING; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -public class DesiredNodesUpgradeIT extends ParameterizedRollingUpgradeTestCase { - - private final int desiredNodesVersion; - - public DesiredNodesUpgradeIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - desiredNodesVersion = Objects.requireNonNullElse(upgradeNode, -1) + 2; - } - +public class DesiredNodesUpgradeIT extends AbstractRollingTestCase { private enum ProcessorsPrecision { DOUBLE, FLOAT } public void testUpgradeDesiredNodes() throws Exception { - assumeTrue("Desired nodes was introduced in 8.1", getOldClusterVersion().onOrAfter(Version.V_8_1_0)); + assumeTrue("Desired nodes was introduced in 8.1", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_1_0)); - if (getOldClusterVersion().onOrAfter(Processors.DOUBLE_PROCESSORS_SUPPORT_VERSION)) { + if (UPGRADE_FROM_VERSION.onOrAfter(Processors.DOUBLE_PROCESSORS_SUPPORT_VERSION)) { assertUpgradedNodesCanReadDesiredNodes(); - } else if (getOldClusterVersion().onOrAfter(DesiredNode.RANGE_FLOAT_PROCESSORS_SUPPORT_VERSION)) { + } else if (UPGRADE_FROM_VERSION.onOrAfter(DesiredNode.RANGE_FLOAT_PROCESSORS_SUPPORT_VERSION)) { assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent(); } else { assertDesiredNodesWithFloatProcessorsAreRejectedInOlderVersions(); @@ -61,7 +50,13 @@ public void testUpgradeDesiredNodes() throws Exception { } private void assertUpgradedNodesCanReadDesiredNodes() throws Exception { - if (isMixedCluster() || isUpgradedCluster()) { + final int desiredNodesVersion = switch (CLUSTER_TYPE) { + case OLD -> 1; + case MIXED -> FIRST_MIXED_ROUND ? 2 : 3; + case UPGRADED -> 4; + }; + + if (CLUSTER_TYPE != ClusterType.OLD) { final Map desiredNodes = getLatestDesiredNodes(); final String historyId = extractValue(desiredNodes, "history_id"); final int version = extractValue(desiredNodes, "version"); @@ -88,7 +83,13 @@ private void assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent() throws ) .toList(); - if (isMixedCluster() || isUpgradedCluster()) { + final int desiredNodesVersion = switch (CLUSTER_TYPE) { + case OLD -> 1; + case MIXED -> FIRST_MIXED_ROUND ? 2 : 3; + case UPGRADED -> 4; + }; + + if (CLUSTER_TYPE != ClusterType.OLD) { updateDesiredNodes(desiredNodes, desiredNodesVersion - 1); } for (int i = 0; i < 2; i++) { @@ -99,25 +100,28 @@ private void assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent() throws final int latestDesiredNodesVersion = extractValue(latestDesiredNodes, "version"); assertThat(latestDesiredNodesVersion, is(equalTo(desiredNodesVersion))); - if (isUpgradedCluster()) { + if (CLUSTER_TYPE == ClusterType.UPGRADED) { assertAllDesiredNodesAreActualized(); } } private void assertDesiredNodesWithFloatProcessorsAreRejectedInOlderVersions() throws Exception { - if (isOldCluster()) { - addClusterNodesToDesiredNodesWithIntegerProcessors(1); - } else if (isMixedCluster()) { - // Processor ranges or float processors are forbidden during upgrades: 8.2 -> 8.3 clusters - final var responseException = expectThrows( - ResponseException.class, - () -> addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(desiredNodesVersion, ProcessorsPrecision.FLOAT) - ); - final var statusCode = responseException.getResponse().getStatusLine().getStatusCode(); - assertThat(statusCode, is(equalTo(400))); - } else { - assertAllDesiredNodesAreActualized(); - addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(4, ProcessorsPrecision.FLOAT); + switch (CLUSTER_TYPE) { + case OLD -> addClusterNodesToDesiredNodesWithIntegerProcessors(1); + case MIXED -> { + int version = FIRST_MIXED_ROUND ? 2 : 3; + // Processor ranges or float processors are forbidden during upgrades: 8.2 -> 8.3 clusters + final var responseException = expectThrows( + ResponseException.class, + () -> addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(version, ProcessorsPrecision.FLOAT) + ); + final var statusCode = responseException.getResponse().getStatusLine().getStatusCode(); + assertThat(statusCode, is(equalTo(400))); + } + case UPGRADED -> { + assertAllDesiredNodesAreActualized(); + addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(4, ProcessorsPrecision.FLOAT); + } } getLatestDesiredNodes(); diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java similarity index 90% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java index 307e9946b7601..588802fb50709 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.elasticsearch.action.admin.cluster.migration.TransportGetFeatureUpgradeStatusAction; import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; @@ -23,17 +21,14 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -public class FeatureUpgradeIT extends ParameterizedRollingUpgradeTestCase { - - public FeatureUpgradeIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } +public class FeatureUpgradeIT extends AbstractRollingTestCase { + @SuppressWarnings("unchecked") public void testGetFeatureUpgradeStatus() throws Exception { final String systemIndexWarning = "this request accesses system indices: [.tasks], but in a future major version, direct " + "access to system indices will be prevented by default"; - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { // setup - put something in the tasks index // create index Request createTestIndex = new Request("PUT", "/feature_test_index_old"); @@ -84,7 +79,7 @@ public void testGetFeatureUpgradeStatus() throws Exception { } }); - } else if (isUpgradedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { // check results assertBusy(() -> { Request clusterStateRequest = new Request("GET", "/_migration/system_features"); @@ -100,7 +95,7 @@ public void testGetFeatureUpgradeStatus() throws Exception { assertThat(feature, aMapWithSize(4)); assertThat(feature.get("minimum_index_version"), equalTo(getOldClusterIndexVersion().toString())); - if (getOldClusterVersion().before(TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION)) { + if (UPGRADE_FROM_VERSION.before(TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION)) { assertThat(feature.get("migration_status"), equalTo("MIGRATION_NEEDED")); } else { assertThat(feature.get("migration_status"), equalTo("NO_MIGRATION_NEEDED")); @@ -108,4 +103,5 @@ public void testGetFeatureUpgradeStatus() throws Exception { }); } } + } diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java similarity index 94% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java index 333cff3c4e039..83865222a8867 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.apache.http.HttpHost; import org.elasticsearch.Version; import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse; @@ -38,17 +36,15 @@ * In 8.2 we also added the ability to filter fields by type and metadata, with some post-hoc filtering applied on * the co-ordinating node if older nodes were included in the system */ -public class FieldCapsIT extends ParameterizedRollingUpgradeTestCase { - - public FieldCapsIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } - - private static boolean oldIndicesCreated; - private static boolean newIndicesCreated; +public class FieldCapsIT extends AbstractRollingTestCase { + private static boolean indicesCreated = false; @Before public void setupIndices() throws Exception { + if (indicesCreated) { + return; + } + indicesCreated = true; final String redMapping = """ "properties": { "red_field": { "type": "keyword" }, @@ -67,7 +63,7 @@ public void setupIndices() throws Exception { "timestamp": {"type": "date"} } """; - if (isOldCluster() && oldIndicesCreated == false) { + if (CLUSTER_TYPE == ClusterType.OLD) { createIndex("old_red_1", Settings.EMPTY, redMapping); createIndex("old_red_2", Settings.EMPTY, redMapping); createIndex("old_red_empty", Settings.EMPTY, redMapping); @@ -82,8 +78,7 @@ public void setupIndices() throws Exception { ); assertOK(client().performRequest(indexRequest)); } - oldIndicesCreated = true; - } else if (isFirstMixedCluster() && newIndicesCreated == false) { + } else if (CLUSTER_TYPE == ClusterType.MIXED && FIRST_MIXED_ROUND) { createIndex("new_red_1", Settings.EMPTY, redMapping); createIndex("new_red_2", Settings.EMPTY, redMapping); createIndex("new_red_empty", Settings.EMPTY, redMapping); @@ -98,7 +93,6 @@ public void setupIndices() throws Exception { ); assertOK(client().performRequest(indexRequest)); } - newIndicesCreated = true; } } @@ -155,7 +149,7 @@ public void testOldIndicesWithIndexFilter() throws Exception { } public void testNewIndicesOnly() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); { FieldCapabilitiesResponse resp = fieldCaps(List.of("new_red_*"), List.of("*"), null, null, null); assertThat(resp.getIndices(), equalTo(new String[] { "new_red_1", "new_red_2", "new_red_empty" })); @@ -183,7 +177,7 @@ public void testNewIndicesOnly() throws Exception { } public void testNewIndicesOnlyWithIndexFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); final QueryBuilder indexFilter = QueryBuilders.rangeQuery("timestamp").gte("2020-01-01").lte("2020-12-12"); { FieldCapabilitiesResponse resp = fieldCaps(List.of("new_red_*"), List.of("*"), indexFilter, null, null); @@ -209,7 +203,7 @@ public void testNewIndicesOnlyWithIndexFilter() throws Exception { } public void testAllIndices() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); FieldCapabilitiesResponse resp = fieldCaps(List.of("old_*", "new_*"), List.of("*"), null, null, null); assertThat( resp.getIndices(), @@ -241,7 +235,7 @@ public void testAllIndices() throws Exception { } public void testAllIndicesWithIndexFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); final QueryBuilder indexFilter = QueryBuilders.rangeQuery("timestamp").gte("2020-01-01").lte("2020-12-12"); FieldCapabilitiesResponse resp = fieldCaps(List.of("old_*", "new_*"), List.of("*"), indexFilter, null, null); assertThat( @@ -291,7 +285,7 @@ private RestClient getUpgradedNodeClient() throws IOException { // because we are testing that the upgraded node will correctly apply filtering // to responses from older nodes that don't understand the filter parameters public void testAllIndicesWithFieldTypeFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); RestClient restClient = getUpgradedNodeClient(); FieldCapabilitiesResponse resp = fieldCaps(restClient, List.of("old_*", "new_*"), List.of("*"), null, "keyword", null); assertThat(resp.getField("red_field").keySet(), contains("keyword")); @@ -304,7 +298,7 @@ public void testAllIndicesWithFieldTypeFilter() throws Exception { // because we are testing that the upgraded node will correctly apply filtering // to responses from older nodes that don't understand the filter parameters public void testAllIndicesWithExclusionFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", isOldCluster()); + assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); RestClient client = getUpgradedNodeClient(); { FieldCapabilitiesResponse resp = fieldCaps(client, List.of("old_*", "new_*"), List.of("*"), null, null, null); diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java similarity index 52% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java index ecd327b0d66c8..b860e53d447b5 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java @@ -7,8 +7,6 @@ */ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.apache.http.util.EntityUtils; import org.elasticsearch.Version; import org.elasticsearch.client.Request; @@ -17,6 +15,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.core.Booleans; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.test.ListMatcher; import org.elasticsearch.xcontent.XContentBuilder; @@ -41,36 +40,39 @@ /** * Basic test that indexed documents survive the rolling restart. See - * {@code RecoveryIT} for much more in depth testing of the mechanism + * {@link RecoveryIT} for much more in depth testing of the mechanism * by which they survive. *

* This test is an almost exact copy of IndexingIT in the * xpack rolling restart tests. We should work on a way to remove this * duplication but for now we have no real way to share code. */ -public class IndexingIT extends ParameterizedRollingUpgradeTestCase { - - public IndexingIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } +public class IndexingIT extends AbstractRollingTestCase { public void testIndexing() throws IOException { - if (isMixedCluster()) { - Request waitForYellow = new Request("GET", "/_cluster/health"); - waitForYellow.addParameter("wait_for_nodes", "3"); - waitForYellow.addParameter("wait_for_status", "yellow"); - client().performRequest(waitForYellow); - } else if (isUpgradedCluster()) { - Request waitForGreen = new Request("GET", "/_cluster/health/test_index,index_with_replicas,empty_index"); - waitForGreen.addParameter("wait_for_nodes", "3"); - waitForGreen.addParameter("wait_for_status", "green"); - // wait for long enough that we give delayed unassigned shards to stop being delayed - waitForGreen.addParameter("timeout", "70s"); - waitForGreen.addParameter("level", "shards"); - client().performRequest(waitForGreen); + switch (CLUSTER_TYPE) { + case OLD: + break; + case MIXED: + Request waitForYellow = new Request("GET", "/_cluster/health"); + waitForYellow.addParameter("wait_for_nodes", "3"); + waitForYellow.addParameter("wait_for_status", "yellow"); + client().performRequest(waitForYellow); + break; + case UPGRADED: + Request waitForGreen = new Request("GET", "/_cluster/health/test_index,index_with_replicas,empty_index"); + waitForGreen.addParameter("wait_for_nodes", "3"); + waitForGreen.addParameter("wait_for_status", "green"); + // wait for long enough that we give delayed unassigned shards to stop being delayed + waitForGreen.addParameter("timeout", "70s"); + waitForGreen.addParameter("level", "shards"); + client().performRequest(waitForGreen); + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); } - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { Request createTestIndex = new Request("PUT", "/test_index"); createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); useIgnoreMultipleMatchingTemplatesWarningsHandler(createTestIndex); @@ -93,20 +95,30 @@ public void testIndexing() throws IOException { } int expectedCount; - if (isOldCluster() || isFirstMixedCluster()) { - expectedCount = 5; - } else if (isMixedCluster()) { - expectedCount = 10; - } else { - expectedCount = 15; + switch (CLUSTER_TYPE) { + case OLD: + expectedCount = 5; + break; + case MIXED: + if (Booleans.parseBoolean(System.getProperty("tests.first_round"))) { + expectedCount = 5; + } else { + expectedCount = 10; + } + break; + case UPGRADED: + expectedCount = 15; + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); } assertCount("test_index", expectedCount); assertCount("index_with_replicas", 5); assertCount("empty_index", 0); - if (isOldCluster() == false) { - bulk("test_index", "_" + (isMixedCluster() ? "MIXED" : "UPGRADED"), 5); + if (CLUSTER_TYPE != ClusterType.OLD) { + bulk("test_index", "_" + CLUSTER_TYPE, 5); Request toBeDeleted = new Request("PUT", "/test_index/_doc/to_be_deleted"); toBeDeleted.addParameter("refresh", "true"); toBeDeleted.setJsonEntity("{\"f1\": \"delete-me\"}"); @@ -131,76 +143,82 @@ public void testAutoIdWithOpTypeCreate() throws IOException { bulk.addParameter("refresh", "true"); bulk.setJsonEntity(b); - if (isOldCluster()) { - Request createTestIndex = new Request("PUT", "/" + indexName); - createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); - client().performRequest(createTestIndex); - } else if (isMixedCluster()) { - Request waitForGreen = new Request("GET", "/_cluster/health"); - waitForGreen.addParameter("wait_for_nodes", "3"); - client().performRequest(waitForGreen); - Version minNodeVersion = minNodeVersion(); - if (minNodeVersion.before(Version.V_7_5_0)) { - ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(bulk)); - assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); - assertThat( - e.getMessage(), - // if request goes to 7.5+ node - either(containsString("optype create not supported for indexing requests without explicit id until")) - // if request goes to < 7.5 node - .or(containsString("an id must be provided if version type or value are set")) - ); - } else { - client().performRequest(bulk); + switch (CLUSTER_TYPE) { + case OLD -> { + Request createTestIndex = new Request("PUT", "/" + indexName); + createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); + client().performRequest(createTestIndex); } - } else if (isUpgradedCluster()) { - client().performRequest(bulk); + case MIXED -> { + Request waitForGreen = new Request("GET", "/_cluster/health"); + waitForGreen.addParameter("wait_for_nodes", "3"); + client().performRequest(waitForGreen); + Version minNodeVersion = minNodeVersion(); + if (minNodeVersion.before(Version.V_7_5_0)) { + ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(bulk)); + assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); + assertThat( + e.getMessage(), + // if request goes to 7.5+ node + either(containsString("optype create not supported for indexing requests without explicit id until")) + // if request goes to < 7.5 node + .or(containsString("an id must be provided if version type or value are set")) + ); + } else { + client().performRequest(bulk); + } + } + case UPGRADED -> client().performRequest(bulk); + default -> throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); } } public void testDateNanosFormatUpgrade() throws IOException { final String indexName = "test_date_nanos"; - if (isOldCluster()) { - Request createIndex = new Request("PUT", "/" + indexName); - XContentBuilder mappings = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .startObject("mappings") - .startObject("properties") - .startObject("date") - .field("type", "date") - .endObject() - .startObject("date_nanos") - .field("type", "date_nanos") - .endObject() - .endObject() - .endObject() - .endObject(); - createIndex.setJsonEntity(Strings.toString(mappings)); - client().performRequest(createIndex); - Request index = new Request("POST", "/" + indexName + "/_doc/"); - XContentBuilder doc = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .field("date", "2015-01-01T12:10:30.123456789Z") - .field("date_nanos", "2015-01-01T12:10:30.123456789Z") - .endObject(); - index.addParameter("refresh", "true"); - index.setJsonEntity(Strings.toString(doc)); - client().performRequest(index); - } else if (isUpgradedCluster()) { - Request search = new Request("POST", "/" + indexName + "/_search"); - XContentBuilder query = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .array("fields", new String[] { "date", "date_nanos" }) - .endObject(); - search.setJsonEntity(Strings.toString(query)); - Map response = entityAsMap(client().performRequest(search)); - Map bestHit = (Map) ((List) (XContentMapValues.extractValue("hits.hits", response))).get(0); - List date = (List) XContentMapValues.extractValue("fields.date", bestHit); - assertThat(date.size(), equalTo(1)); - assertThat(date.get(0), equalTo("2015-01-01T12:10:30.123Z")); - List dateNanos = (List) XContentMapValues.extractValue("fields.date_nanos", bestHit); - assertThat(dateNanos.size(), equalTo(1)); - assertThat(dateNanos.get(0), equalTo("2015-01-01T12:10:30.123456789Z")); + switch (CLUSTER_TYPE) { + case OLD -> { + Request createIndex = new Request("PUT", "/" + indexName); + XContentBuilder mappings = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .startObject("mappings") + .startObject("properties") + .startObject("date") + .field("type", "date") + .endObject() + .startObject("date_nanos") + .field("type", "date_nanos") + .endObject() + .endObject() + .endObject() + .endObject(); + createIndex.setJsonEntity(Strings.toString(mappings)); + client().performRequest(createIndex); + Request index = new Request("POST", "/" + indexName + "/_doc/"); + XContentBuilder doc = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .field("date", "2015-01-01T12:10:30.123456789Z") + .field("date_nanos", "2015-01-01T12:10:30.123456789Z") + .endObject(); + index.addParameter("refresh", "true"); + index.setJsonEntity(Strings.toString(doc)); + client().performRequest(index); + } + case UPGRADED -> { + Request search = new Request("POST", "/" + indexName + "/_search"); + XContentBuilder query = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .array("fields", new String[] { "date", "date_nanos" }) + .endObject(); + search.setJsonEntity(Strings.toString(query)); + Map response = entityAsMap(client().performRequest(search)); + Map bestHit = (Map) ((List) (XContentMapValues.extractValue("hits.hits", response))).get(0); + List date = (List) XContentMapValues.extractValue("fields.date", bestHit); + assertThat(date.size(), equalTo(1)); + assertThat(date.get(0), equalTo("2015-01-01T12:10:30.123Z")); + List dateNanos = (List) XContentMapValues.extractValue("fields.date_nanos", bestHit); + assertThat(dateNanos.size(), equalTo(1)); + assertThat(dateNanos.get(0), equalTo("2015-01-01T12:10:30.123456789Z")); + } } } @@ -229,45 +247,51 @@ private void bulk(String index, String valueSuffix, int count) throws IOExceptio } public void testTsdb() throws IOException { - assumeTrue("indexing time series indices changed in 8.2.0", getOldClusterVersion().onOrAfter(Version.V_8_2_0)); + assumeTrue("indexing time series indices changed in 8.2.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_2_0)); StringBuilder bulk = new StringBuilder(); - if (isOldCluster()) { - createTsdbIndex(); - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[0], TSDB_TIMES[1], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[0], TSDB_TIMES[1], -0.1); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(215.95, 0.005), closeTo(-215.95, 0.005)); - return; - } else if (isFirstMixedCluster()) { - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[1], TSDB_TIMES[2], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[1], TSDB_TIMES[2], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[0], TSDB_TIMES[2], 1.1); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(217.45, 0.005), closeTo(-217.45, 0.005), closeTo(2391.95, 0.005)); - - } else if (isMixedCluster()) { - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[2], TSDB_TIMES[3], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[2], TSDB_TIMES[3], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[2], TSDB_TIMES[3], 1.1); - tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[0], TSDB_TIMES[3], 10); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(218.95, 0.005), closeTo(-218.95, 0.005), closeTo(2408.45, 0.005), closeTo(21895, 0.5)); - return; - } else { - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[3], TSDB_TIMES[4], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[3], TSDB_TIMES[4], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[3], TSDB_TIMES[4], 1.1); - tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[3], TSDB_TIMES[4], 10); - tsdbBulk(bulk, TSDB_DIMS.get(4), TSDB_TIMES[0], TSDB_TIMES[4], -5); - bulk("tsdb", bulk.toString()); - assertTsdbAgg( - closeTo(220.45, 0.005), - closeTo(-220.45, 0.005), - closeTo(2424.95, 0.005), - closeTo(22045, 0.5), - closeTo(-11022.5, 0.5) - ); + switch (CLUSTER_TYPE) { + case OLD -> { + createTsdbIndex(); + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[0], TSDB_TIMES[1], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[0], TSDB_TIMES[1], -0.1); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(215.95, 0.005), closeTo(-215.95, 0.005)); + return; + } + case MIXED -> { + if (FIRST_MIXED_ROUND) { + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[1], TSDB_TIMES[2], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[1], TSDB_TIMES[2], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[0], TSDB_TIMES[2], 1.1); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(217.45, 0.005), closeTo(-217.45, 0.005), closeTo(2391.95, 0.005)); + return; + } + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[2], TSDB_TIMES[3], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[2], TSDB_TIMES[3], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[2], TSDB_TIMES[3], 1.1); + tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[0], TSDB_TIMES[3], 10); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(218.95, 0.005), closeTo(-218.95, 0.005), closeTo(2408.45, 0.005), closeTo(21895, 0.5)); + return; + } + case UPGRADED -> { + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[3], TSDB_TIMES[4], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[3], TSDB_TIMES[4], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[3], TSDB_TIMES[4], 1.1); + tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[3], TSDB_TIMES[4], 10); + tsdbBulk(bulk, TSDB_DIMS.get(4), TSDB_TIMES[0], TSDB_TIMES[4], -5); + bulk("tsdb", bulk.toString()); + assertTsdbAgg( + closeTo(220.45, 0.005), + closeTo(-220.45, 0.005), + closeTo(2424.95, 0.005), + closeTo(22045, 0.5), + closeTo(-11022.5, 0.5) + ); + return; + } } } @@ -337,60 +361,67 @@ private void assertTsdbAgg(Matcher... expected) throws IOException { } public void testSyntheticSource() throws IOException { - assumeTrue("added in 8.4.0", getOldClusterVersion().onOrAfter(Version.V_8_4_0)); - - if (isOldCluster()) { - Request createIndex = new Request("PUT", "/synthetic"); - XContentBuilder indexSpec = XContentBuilder.builder(XContentType.JSON.xContent()).startObject(); - indexSpec.startObject("mappings"); - { - indexSpec.startObject("_source").field("mode", "synthetic").endObject(); - indexSpec.startObject("properties").startObject("kwd").field("type", "keyword").endObject().endObject(); + assumeTrue("added in 8.4.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_4_0)); + + switch (CLUSTER_TYPE) { + case OLD -> { + Request createIndex = new Request("PUT", "/synthetic"); + XContentBuilder indexSpec = XContentBuilder.builder(XContentType.JSON.xContent()).startObject(); + indexSpec.startObject("mappings"); + { + indexSpec.startObject("_source").field("mode", "synthetic").endObject(); + indexSpec.startObject("properties").startObject("kwd").field("type", "keyword").endObject().endObject(); + } + indexSpec.endObject(); + createIndex.setJsonEntity(Strings.toString(indexSpec.endObject())); + client().performRequest(createIndex); + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "old"}} + {"kwd": "old", "int": -12} + """); + break; + } + case MIXED -> { + if (FIRST_MIXED_ROUND) { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "mixed_1"}} + {"kwd": "mixed_1", "int": 22} + """); + } else { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "mixed_2"}} + {"kwd": "mixed_2", "int": 33} + """); + } + break; + } + case UPGRADED -> { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "new"}} + {"kwd": "new", "int": 21341325} + """); } - indexSpec.endObject(); - createIndex.setJsonEntity(Strings.toString(indexSpec.endObject())); - client().performRequest(createIndex); - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "old"}} - {"kwd": "old", "int": -12} - """); - } else if (isFirstMixedCluster()) { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "mixed_1"}} - {"kwd": "mixed_1", "int": 22} - """); - } else if (isMixedCluster()) { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "mixed_2"}} - {"kwd": "mixed_2", "int": 33} - """); - - } else { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "new"}} - {"kwd": "new", "int": 21341325} - """); } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/old"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "old").entry("int", -12)) ); - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { return; } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/mixed_1"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "mixed_1").entry("int", 22)) ); - if (isFirstMixedCluster()) { + if (CLUSTER_TYPE == ClusterType.MIXED && FIRST_MIXED_ROUND) { return; } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/mixed_2"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "mixed_2").entry("int", 33)) ); - if (isMixedCluster()) { + if (CLUSTER_TYPE == ClusterType.MIXED) { return; } assertMap( diff --git a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java similarity index 97% rename from qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java index 077eae88fba02..39700514cd79f 100644 --- a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java @@ -41,6 +41,7 @@ import static org.elasticsearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING; import static org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider.INDEX_ROUTING_ALLOCATION_ENABLE_SETTING; import static org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY; +import static org.elasticsearch.upgrades.UpgradeWithOldIndexSettingsIT.updateIndexSettingsPermittingSlowlogDeprecationWarning; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.in; @@ -746,23 +747,4 @@ public void testSoftDeletesDisabledWarning() throws Exception { ensureGreen(indexName); indexDocs(indexName, randomInt(100), randomInt(100)); } - - /* - * Copied from UpgradeWithOldIndexSettingsIT in the new format - */ - private static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { - Request request = new Request("PUT", "/" + index + "/_settings"); - request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); - if (UPGRADE_FROM_VERSION.before(Version.V_7_17_9)) { - // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) - // Below warnings are set (and leaking) from an index in this test case - request.setOptions(expectVersionSpecificWarnings(v -> { - v.compatible( - "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " - + "See the breaking changes documentation for the next major version." - ); - })); - } - client().performRequest(request); - } } diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java similarity index 61% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java index a01d379f68e76..b8ce7fe7a6fbb 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.util.EntityUtils; @@ -42,102 +40,100 @@ import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; -public class SnapshotBasedRecoveryIT extends ParameterizedRollingUpgradeTestCase { - - public SnapshotBasedRecoveryIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } +public class SnapshotBasedRecoveryIT extends AbstractRollingTestCase { public void testSnapshotBasedRecovery() throws Exception { assumeFalse( "Cancel shard allocation command is broken for initial desired balance versions and might allocate shard " + "on the node where it is not supposed to be. Fixed by https://github.com/elastic/elasticsearch/pull/93635", - getOldClusterVersion() == Version.V_8_6_0 - || getOldClusterVersion() == Version.V_8_6_1 - || getOldClusterVersion() == Version.V_8_7_0 + UPGRADE_FROM_VERSION == Version.V_8_6_0 || UPGRADE_FROM_VERSION == Version.V_8_6_1 || UPGRADE_FROM_VERSION == Version.V_8_7_0 ); final String indexName = "snapshot_based_recovery"; final String repositoryName = "snapshot_based_recovery_repo"; final int numDocs = 200; - if (isOldCluster()) { - Settings.Builder settings = Settings.builder() - .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) - .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) - .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") - .put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster - createIndex(indexName, settings.build()); - ensureGreen(indexName); - indexDocs(indexName, numDocs); - flush(indexName, true); - registerRepository( - repositoryName, - "fs", - true, - Settings.builder() - .put("location", "./snapshot_based_recovery") - .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), true) - .build() - ); - createSnapshot(repositoryName, "snap", true); - updateIndexSettings(indexName, Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)); - ensureGreen(indexName); - } else { - if (isFirstMixedCluster()) { - List upgradedNodeIds = getUpgradedNodeIds(); - // It's possible that the test simply does a rolling-restart, i.e. it "upgrades" to - // the same version. In that case we proceed without excluding any node - if (upgradedNodeIds.isEmpty() == false) { - assertThat(upgradedNodeIds.size(), is(equalTo(1))); - String upgradedNodeId = upgradedNodeIds.get(0); - logger.info("--> excluding [{}] from node [{}]", indexName, upgradedNodeId); - updateIndexSettings(indexName, Settings.builder().put("index.routing.allocation.exclude._id", upgradedNodeId)); - ensureGreen(indexName); - logger.info("--> finished excluding [{}] from node [{}]", indexName, upgradedNodeId); - } else { - logger.info("--> no upgrading nodes, not adding any exclusions for [{}]", indexName); - } + switch (CLUSTER_TYPE) { + case OLD -> { + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") + .put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster + createIndex(indexName, settings.build()); + ensureGreen(indexName); + indexDocs(indexName, numDocs); + flush(indexName, true); + registerRepository( + repositoryName, + "fs", + true, + Settings.builder() + .put("location", "./snapshot_based_recovery") + .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), true) + .build() + ); + createSnapshot(repositoryName, "snap", true); + updateIndexSettings(indexName, Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)); + ensureGreen(indexName); + } + case MIXED, UPGRADED -> { + if (FIRST_MIXED_ROUND) { + List upgradedNodeIds = getUpgradedNodeIds(); + // It's possible that the test simply does a rolling-restart, i.e. it "upgrades" to + // the same version. In that case we proceed without excluding any node + if (upgradedNodeIds.isEmpty() == false) { + assertThat(upgradedNodeIds.size(), is(equalTo(1))); + String upgradedNodeId = upgradedNodeIds.get(0); + logger.info("--> excluding [{}] from node [{}]", indexName, upgradedNodeId); + updateIndexSettings(indexName, Settings.builder().put("index.routing.allocation.exclude._id", upgradedNodeId)); + ensureGreen(indexName); + logger.info("--> finished excluding [{}] from node [{}]", indexName, upgradedNodeId); + } else { + logger.info("--> no upgrading nodes, not adding any exclusions for [{}]", indexName); + } - String primaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - Version primaryNodeVersion = getNodeVersion(primaryNodeId); + String primaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); + Version primaryNodeVersion = getNodeVersion(primaryNodeId); - // Sometimes the primary shard ends on the upgraded node (i.e. after a rebalance) - // This causes issues when removing and adding replicas, since then we cannot allocate to any of the old nodes. - // That is an issue only for the first mixed round. - // In that case we exclude the upgraded node from the shard allocation and cancel the shard to force moving - // the primary to a node in the old version, this allows adding replicas in the first mixed round. - logger.info("--> Primary node in first mixed round {} / {}", primaryNodeId, primaryNodeVersion); - if (primaryNodeVersion.after(getOldClusterVersion())) { - logger.info("--> cancelling primary shard on node [{}]", primaryNodeId); - cancelShard(indexName, 0, primaryNodeId); - logger.info("--> done cancelling primary shard on node [{}]", primaryNodeId); + // Sometimes the primary shard ends on the upgraded node (i.e. after a rebalance) + // This causes issues when removing and adding replicas, since then we cannot allocate to any of the old nodes. + // That is an issue only for the first mixed round. + // In that case we exclude the upgraded node from the shard allocation and cancel the shard to force moving + // the primary to a node in the old version, this allows adding replicas in the first mixed round. + logger.info("--> Primary node in first mixed round {} / {}", primaryNodeId, primaryNodeVersion); + if (primaryNodeVersion.after(UPGRADE_FROM_VERSION)) { + logger.info("--> cancelling primary shard on node [{}]", primaryNodeId); + cancelShard(indexName, 0, primaryNodeId); + logger.info("--> done cancelling primary shard on node [{}]", primaryNodeId); - String currentPrimaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - assertThat(getNodeVersion(currentPrimaryNodeId), is(equalTo(getOldClusterVersion()))); + String currentPrimaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); + assertThat(getNodeVersion(currentPrimaryNodeId), is(equalTo(UPGRADE_FROM_VERSION))); + } + } else { + logger.info("--> not in first upgrade round, removing exclusions for [{}]", indexName); + updateIndexSettings(indexName, Settings.builder().putNull("index.routing.allocation.exclude._id")); + logger.info("--> done removing exclusions for [{}]", indexName); } - } else { - logger.info("--> not in first upgrade round, removing exclusions for [{}]", indexName); - updateIndexSettings(indexName, Settings.builder().putNull("index.routing.allocation.exclude._id")); - logger.info("--> done removing exclusions for [{}]", indexName); - } - // Drop replicas - logger.info("--> dropping replicas from [{}]", indexName); - updateIndexSettingsPermittingSlowlogDeprecationWarning( - indexName, - Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) - ); - logger.info("--> finished dropping replicas from [{}], adding them back", indexName); - updateIndexSettingsPermittingSlowlogDeprecationWarning( - indexName, - Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) - ); - logger.info("--> finished adding replicas from [{}]", indexName); - ensureGreen(indexName); + // Drop replicas + logger.info("--> dropping replicas from [{}]", indexName); + updateIndexSettingsPermittingSlowlogDeprecationWarning( + indexName, + Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + ); + logger.info("--> finished dropping replicas from [{}], adding them back", indexName); + updateIndexSettingsPermittingSlowlogDeprecationWarning( + indexName, + Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) + ); + logger.info("--> finished adding replicas from [{}]", indexName); + ensureGreen(indexName); - assertMatchAllReturnsAllDocuments(indexName, numDocs); - assertMatchQueryReturnsAllDocuments(indexName, numDocs); + assertMatchAllReturnsAllDocuments(indexName, numDocs); + assertMatchQueryReturnsAllDocuments(indexName, numDocs); + } + default -> throw new IllegalStateException("unknown type " + CLUSTER_TYPE); } } @@ -149,7 +145,7 @@ private List getUpgradedNodeIds() throws IOException { List upgradedNodes = new ArrayList<>(); for (Map.Entry> nodeInfoEntry : nodes.entrySet()) { Version nodeVersion = Version.fromString(extractValue(nodeInfoEntry.getValue(), "version")); - if (nodeVersion.after(getOldClusterVersion())) { + if (nodeVersion.after(UPGRADE_FROM_VERSION)) { upgradedNodes.add(nodeInfoEntry.getKey()); } } diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java similarity index 95% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java index f6de3f4e48d68..48c3007bb2674 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; import org.elasticsearch.index.IndexVersion; @@ -23,17 +21,13 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -public class SystemIndicesUpgradeIT extends ParameterizedRollingUpgradeTestCase { - - public SystemIndicesUpgradeIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } +public class SystemIndicesUpgradeIT extends AbstractRollingTestCase { @SuppressWarnings("unchecked") public void testSystemIndicesUpgrades() throws Exception { final String systemIndexWarning = "this request accesses system indices: [.tasks], but in a future major version, direct " + "access to system indices will be prevented by default"; - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { // create index Request createTestIndex = new Request("PUT", "/test_index_old"); createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); @@ -105,7 +99,7 @@ public void testSystemIndicesUpgrades() throws Exception { })); assertThat(client().performRequest(putAliasRequest).getStatusLine().getStatusCode(), is(200)); } - } else if (isUpgradedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { assertBusy(() -> { Request clusterStateRequest = new Request("GET", "/_cluster/state/metadata"); Map indices = new JsonMapView(entityAsMap(client().performRequest(clusterStateRequest))).get( diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java similarity index 90% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java index f8464be894ac9..19f24c97a47f8 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.common.time.DateFormatter; @@ -26,11 +24,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class TsdbIT extends ParameterizedRollingUpgradeTestCase { - - public TsdbIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } +public class TsdbIT extends AbstractRollingTestCase { private static final String TEMPLATE = """ { @@ -94,21 +88,21 @@ public TsdbIT(@Name("upgradeNode") Integer upgradeNode) { private static final String BULK = """ {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959507", "ip": "10.10.55.1", "network": {"tx": 2001818691, "rx": 802133794}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959507","ip": "10.10.55.1", "network": {"tx": 2001818691, "rx": 802133794}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "hamster", "uid":"947e4ced-1786-4e53-9e0c-5c447e959508", "ip": "10.10.55.1", "network": {"tx": 2005177954, "rx": 801479970}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "hamster", "uid":"947e4ced-1786-4e53-9e0c-5c447e959508","ip": "10.10.55.1", "network": {"tx": 2005177954, "rx": 801479970}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cow", "uid":"947e4ced-1786-4e53-9e0c-5c447e959509", "ip": "10.10.55.1", "network": {"tx": 2006223737, "rx": 802337279}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cow", "uid":"947e4ced-1786-4e53-9e0c-5c447e959509","ip": "10.10.55.1", "network": {"tx": 2006223737, "rx": 802337279}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "rat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959510", "ip": "10.10.55.2", "network": {"tx": 2012916202, "rx": 803685721}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "rat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959510","ip": "10.10.55.2", "network": {"tx": 2012916202, "rx": 803685721}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "dog", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9", "ip": "10.10.55.3", "network": {"tx": 1434521831, "rx": 530575198}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "dog", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9","ip": "10.10.55.3", "network": {"tx": 1434521831, "rx": 530575198}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "tiger", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea10", "ip": "10.10.55.3", "network": {"tx": 1434577921, "rx": 530600088}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "tiger", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea10","ip": "10.10.55.3", "network": {"tx": 1434577921, "rx": 530600088}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "lion", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876e11", "ip": "10.10.55.3", "network": {"tx": 1434587694, "rx": 530604797}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "lion", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876e11","ip": "10.10.55.3", "network": {"tx": 1434587694, "rx": 530604797}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "elephant", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876eb4", "ip": "10.10.55.3", "network": {"tx": 1434595272, "rx": 530605511}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "elephant", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876eb4","ip": "10.10.55.3", "network": {"tx": 1434595272, "rx": 530605511}}}} """; private static final String DOC = """ @@ -131,11 +125,11 @@ public TsdbIT(@Name("upgradeNode") Integer upgradeNode) { public void testTsdbDataStream() throws Exception { assumeTrue( - "Skipping version [" + getOldClusterVersion() + "], because TSDB was GA-ed in 8.7.0", - getOldClusterVersion().onOrAfter(Version.V_8_7_0) + "Skipping version [" + UPGRADE_FROM_VERSION + "], because TSDB was GA-ed in 8.7.0", + UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_7_0) ); String dataStreamName = "k8s"; - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { final String INDEX_TEMPLATE = """ { "index_patterns": ["$PATTERN"], @@ -150,20 +144,20 @@ public void testTsdbDataStream() throws Exception { assertOK(client().performRequest(putIndexTemplateRequest)); performOldClustertOperations(templateName, dataStreamName); - } else if (isMixedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.MIXED) { performMixedClusterOperations(dataStreamName); - } else if (isUpgradedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { performUpgradedClusterOperations(dataStreamName); } } public void testTsdbDataStreamWithComponentTemplate() throws Exception { assumeTrue( - "Skipping version [" + getOldClusterVersion() + "], because TSDB was GA-ed in 8.7.0 and bug was fixed in 8.11.0", - getOldClusterVersion().onOrAfter(Version.V_8_7_0) && getOldClusterVersion().before(Version.V_8_11_0) + "Skipping version [" + UPGRADE_FROM_VERSION + "], because TSDB was GA-ed in 8.7.0 and bug was fixed in 8.11.0", + UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_7_0) && UPGRADE_FROM_VERSION.before(Version.V_8_11_0) ); String dataStreamName = "template-with-component-template"; - if (isOldCluster()) { + if (CLUSTER_TYPE == ClusterType.OLD) { final String COMPONENT_TEMPLATE = """ { "template": $TEMPLATE @@ -187,9 +181,9 @@ public void testTsdbDataStreamWithComponentTemplate() throws Exception { assertOK(client().performRequest(putIndexTemplateRequest)); performOldClustertOperations(templateName, dataStreamName); - } else if (isMixedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.MIXED) { performMixedClusterOperations(dataStreamName); - } else if (isUpgradedCluster()) { + } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { performUpgradedClusterOperations(dataStreamName); var dataStreams = getDataStream(dataStreamName); @@ -248,7 +242,7 @@ private void performUpgradedClusterOperations(String dataStreamName) throws Exce private static void performMixedClusterOperations(String dataStreamName) throws IOException { ensureHealth(dataStreamName, request -> request.addParameter("wait_for_status", "yellow")); - if (isFirstMixedCluster()) { + if (FIRST_MIXED_ROUND) { indexDoc(dataStreamName); } assertSearch(dataStreamName, 9); diff --git a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java similarity index 95% rename from qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java index 068747d5a4824..0f829f20fe3c4 100644 --- a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java @@ -40,6 +40,11 @@ protected boolean preserveTemplatesUponCompletion() { return true; } + @Override + protected boolean preserveDataStreamsUponCompletion() { + return true; + } + public UpgradeClusterClientYamlTestSuiteIT(ClientYamlTestCandidate testCandidate) { super(testCandidate); } diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java new file mode 100644 index 0000000000000..35688e7c244cf --- /dev/null +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.upgrades; + +import org.elasticsearch.Version; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.core.Strings; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; +import static org.hamcrest.Matchers.is; + +public class UpgradeWithOldIndexSettingsIT extends AbstractRollingTestCase { + + private static final String INDEX_NAME = "test_index_old_settings"; + private static final String EXPECTED_WARNING = "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will " + + "be removed in a future release! See the breaking changes documentation for the next major version."; + + private static final String EXPECTED_V8_WARNING = "[index.indexing.slowlog.level] setting was deprecated in the previous Elasticsearch" + + " release and is removed in this release."; + + @SuppressWarnings("unchecked") + public void testOldIndexSettings() throws Exception { + switch (CLUSTER_TYPE) { + case OLD -> { + Request createTestIndex = new Request("PUT", "/" + INDEX_NAME); + createTestIndex.setJsonEntity("{\"settings\": {\"index.indexing.slowlog.level\": \"WARN\"}}"); + createTestIndex.setOptions(expectWarnings(EXPECTED_WARNING)); + if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { + // create index with settings no longer valid in 8.0 + client().performRequest(createTestIndex); + } else { + assertTrue( + expectThrows(ResponseException.class, () -> client().performRequest(createTestIndex)).getMessage() + .contains("unknown setting [index.indexing.slowlog.level]") + ); + + Request createTestIndex1 = new Request("PUT", "/" + INDEX_NAME); + client().performRequest(createTestIndex1); + } + + // add some data + Request bulk = new Request("POST", "/_bulk"); + bulk.addParameter("refresh", "true"); + if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { + bulk.setOptions(expectWarnings(EXPECTED_WARNING)); + } + bulk.setJsonEntity(Strings.format(""" + {"index": {"_index": "%s"}} + {"f1": "v1", "f2": "v2"} + """, INDEX_NAME)); + client().performRequest(bulk); + } + case MIXED -> { + // add some more data + Request bulk = new Request("POST", "/_bulk"); + bulk.addParameter("refresh", "true"); + if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { + bulk.setOptions(expectWarnings(EXPECTED_WARNING)); + } + bulk.setJsonEntity(Strings.format(""" + {"index": {"_index": "%s"}} + {"f1": "v3", "f2": "v4"} + """, INDEX_NAME)); + client().performRequest(bulk); + } + case UPGRADED -> { + if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { + Request createTestIndex = new Request("PUT", "/" + INDEX_NAME + "/_settings"); + // update index settings should work + createTestIndex.setJsonEntity("{\"index.indexing.slowlog.level\": \"INFO\"}"); + createTestIndex.setOptions(expectWarnings(EXPECTED_V8_WARNING)); + client().performRequest(createTestIndex); + + // ensure we were able to change the setting, despite it having no effect + Request indexSettingsRequest = new Request("GET", "/" + INDEX_NAME + "/_settings"); + Map response = entityAsMap(client().performRequest(indexSettingsRequest)); + + var slowLogLevel = (String) (XContentMapValues.extractValue( + INDEX_NAME + ".settings.index.indexing.slowlog.level", + response + )); + + // check that we can read our old index settings + assertThat(slowLogLevel, is("INFO")); + } + assertCount(INDEX_NAME, 2); + } + } + } + + private void assertCount(String index, int countAtLeast) throws IOException { + Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); + searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); + searchTestIndexRequest.addParameter("filter_path", "hits.total"); + Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); + Map response = entityAsMap(searchTestIndexResponse); + + var hitsTotal = (Integer) (XContentMapValues.extractValue("hits.total", response)); + + assertTrue(hitsTotal >= countAtLeast); + } + + public static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { + Request request = new Request("PUT", "/" + index + "/_settings"); + request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); + if (UPGRADE_FROM_VERSION.before(Version.V_7_17_9)) { + // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) + // Below warnings are set (and leaking) from an index in this test case + request.setOptions(expectVersionSpecificWarnings(v -> { + v.compatible( + "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " + + "See the breaking changes documentation for the next major version." + ); + })); + } + client().performRequest(request); + } +} diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java similarity index 93% rename from qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java rename to qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java index b63e1147442a7..40e63b4ae32d7 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java @@ -7,8 +7,6 @@ */ package org.elasticsearch.upgrades; -import com.carrotsearch.randomizedtesting.annotations.Name; - import org.apache.http.util.EntityUtils; import org.elasticsearch.client.Request; import org.junit.Before; @@ -22,12 +20,7 @@ * Basic tests for simple xpack functionality that are only run if the * cluster is the on the default distribution. */ -public class XPackIT extends ParameterizedRollingUpgradeTestCase { - - public XPackIT(@Name("upgradeNode") Integer upgradeNode) { - super(upgradeNode); - } - +public class XPackIT extends AbstractRollingTestCase { @Before public void skipIfNotXPack() { assumeThat( @@ -35,9 +28,10 @@ public void skipIfNotXPack() { System.getProperty("tests.distribution"), equalTo("default") ); - assumeTrue( + assumeThat( "running this on the unupgraded cluster would change its state and it wouldn't work prior to 6.3 anyway", - isUpgradedCluster() + CLUSTER_TYPE, + equalTo(ClusterType.UPGRADED) ); /* * *Mostly* we want this for when we're upgrading from pre-6.3's diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml similarity index 99% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml index b471fa56a47a5..11e9fdc2cca95 100644 --- a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml @@ -11,6 +11,7 @@ bdv: type: dense_vector dims: 3 + index: false knn: type: dense_vector dims: 3 @@ -125,6 +126,7 @@ bdv: type: dense_vector element_type: byte + index: false dims: 3 knn: type: dense_vector diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml similarity index 100% rename from qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml rename to qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml From a33ebc6f03f949408d87bd1ca81044f822793d50 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 21 Sep 2023 15:18:27 +0100 Subject: [PATCH 019/155] Migrate IndexShardOperationPermits to proper executor (#99719) Relates #99392, #97879 --- .../TransportReplicationAction.java | 6 +- .../org/elasticsearch/index/IndexService.java | 3 +- .../index/seqno/RetentionLeaseActions.java | 3 +- .../elasticsearch/index/shard/IndexShard.java | 24 +++--- .../shard/IndexShardOperationPermits.java | 27 +++++-- .../recovery/RecoverySourceHandler.java | 2 +- ...TransportResyncReplicationActionTests.java | 5 +- .../TransportReplicationActionTests.java | 7 +- .../TransportWriteActionTests.java | 5 +- .../IndexShardOperationPermitsTests.java | 76 ++++++++----------- .../index/shard/IndexShardTests.java | 46 ++++++----- .../recovery/RecoverySourceHandlerTests.java | 7 +- .../ESIndexLevelReplicationTestCase.java | 11 +-- .../index/shard/IndexShardTestCase.java | 3 + .../action/TransportForgetFollowerAction.java | 3 +- .../ShardFollowTaskReplicationTests.java | 7 +- .../engine/FollowEngineIndexShardTests.java | 3 +- 17 files changed, 128 insertions(+), 110 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index d1e2b6dd98faa..b3f0496597009 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -117,7 +117,7 @@ public abstract class TransportReplicationAction< protected final ShardStateAction shardStateAction; protected final IndicesService indicesService; protected final TransportRequestOptions transportOptions; - protected final String executor; + protected final Executor executor; protected final boolean forceExecutionOnPrimary; // package private for testing @@ -179,7 +179,7 @@ protected TransportReplicationAction( this.clusterService = clusterService; this.indicesService = indicesService; this.shardStateAction = shardStateAction; - this.executor = executor; + this.executor = threadPool.executor(executor); this.transportPrimaryAction = actionName + "[p]"; this.transportReplicaAction = actionName + "[r]"; @@ -272,7 +272,7 @@ protected abstract void shardOperationOnPrimary( /** * Execute the specified replica operation. This is done under a permit from - * {@link IndexShard#acquireReplicaOperationPermit(long, long, long, ActionListener, String)}. + * {@link IndexShard#acquireReplicaOperationPermit(long, long, long, ActionListener, Executor)}. * * @param shardRequest the request to the replica shard * @param replica the replica shard to perform the operation on diff --git a/server/src/main/java/org/elasticsearch/index/IndexService.java b/server/src/main/java/org/elasticsearch/index/IndexService.java index 05c6fd63c3fcb..781ff035a79ff 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexService.java +++ b/server/src/main/java/org/elasticsearch/index/IndexService.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.concurrent.AbstractAsyncTask; import org.elasticsearch.common.util.concurrent.AbstractRunnable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Assertions; import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.core.IOUtils; @@ -1047,7 +1048,7 @@ private void sync(final Consumer sync, final String source) { && e instanceof ShardNotInPrimaryModeException == false) { logger.warn(() -> format("%s failed to execute %s sync", shard.shardId(), source), e); } - }, ThreadPool.Names.SAME); + }, EsExecutors.DIRECT_EXECUTOR_SERVICE); } catch (final AlreadyClosedException | IndexShardClosedException e) { // the shard was closed concurrently, continue } diff --git a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java index 7393371788f8c..2866d1b0ee13a 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Releasable; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.shard.IndexShard; @@ -86,7 +87,7 @@ protected void asyncShardOperation(T request, ShardId shardId, final ActionListe try (Releasable ignore = releasable) { doRetentionLeaseAction(indexShard, request, delegatedListener); } - }), ThreadPool.Names.SAME); + }), EsExecutors.DIRECT_EXECUTOR_SERVICE); } @Override diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 3adee7643990b..c168f3d64f7ea 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -839,8 +839,8 @@ public void onFailure(Exception e) { listener.onFailure(e); } } - }, 30L, TimeUnit.MINUTES, ThreadPool.Names.SAME); // Wait on SAME (current thread) because this execution is wrapped by - // CancellableThreads and we want to be able to safely interrupt it + }, 30L, TimeUnit.MINUTES, EsExecutors.DIRECT_EXECUTOR_SERVICE); // Wait on current thread because this execution is wrapped by + // CancellableThreads and we want to be able to interrupt it } } @@ -3361,11 +3361,15 @@ private EngineConfig newEngineConfig(LongSupplier globalCheckpointSupplier) { * ActionListener will then be called using the provided executor. * */ - public void acquirePrimaryOperationPermit(ActionListener onPermitAcquired, String executorOnDelay) { + public void acquirePrimaryOperationPermit(ActionListener onPermitAcquired, Executor executorOnDelay) { acquirePrimaryOperationPermit(onPermitAcquired, executorOnDelay, false); } - public void acquirePrimaryOperationPermit(ActionListener onPermitAcquired, String executorOnDelay, boolean forceExecution) { + public void acquirePrimaryOperationPermit( + ActionListener onPermitAcquired, + Executor executorOnDelay, + boolean forceExecution + ) { verifyNotClosed(); assert shardRouting.primary() : "acquirePrimaryOperationPermit should only be called on primary shard: " + shardRouting; indexShardOperationPermits.acquire(wrapPrimaryOperationPermitListener(onPermitAcquired), executorOnDelay, forceExecution); @@ -3415,7 +3419,7 @@ private void asyncBlockOperations(ActionListener onPermitAcquired, l onPermitAcquired.onFailure(e); }); try { - indexShardOperationPermits.blockOperations(wrappedListener, timeout, timeUnit, ThreadPool.Names.GENERIC); + indexShardOperationPermits.blockOperations(wrappedListener, timeout, timeUnit, threadPool.generic()); } catch (Exception e) { forceRefreshes.close(); throw e; @@ -3424,7 +3428,7 @@ private void asyncBlockOperations(ActionListener onPermitAcquired, l /** * Runs the specified runnable under a permit and otherwise calling back the specified failure callback. This method is really a - * convenience for {@link #acquirePrimaryOperationPermit(ActionListener, String)} where the listener equates to + * convenience for {@link #acquirePrimaryOperationPermit(ActionListener, Executor)} where the listener equates to * try-with-resources closing the releasable after executing the runnable on successfully acquiring the permit, an otherwise calling * back the failure callback. * @@ -3432,7 +3436,7 @@ private void asyncBlockOperations(ActionListener onPermitAcquired, l * @param onFailure the callback on failure * @param executorOnDelay the executor to execute the runnable on if permit acquisition is blocked */ - public void runUnderPrimaryPermit(final Runnable runnable, final Consumer onFailure, final String executorOnDelay) { + public void runUnderPrimaryPermit(final Runnable runnable, final Consumer onFailure, final Executor executorOnDelay) { verifyNotClosed(); assert shardRouting.primary() : "runUnderPrimaryPermit should only be called on primary shard but was " + shardRouting; final ActionListener onPermitAcquired = ActionListener.wrap(releasable -> { @@ -3505,7 +3509,7 @@ public void onResponse(final Releasable releasable) { /** * Acquire a replica operation permit whenever the shard is ready for indexing (see - * {@link #acquirePrimaryOperationPermit(ActionListener, String)}). If the given primary term is lower than then one in + * {@link #acquirePrimaryOperationPermit(ActionListener, Executor)}). If the given primary term is lower than then one in * {@link #shardRouting}, the {@link ActionListener#onFailure(Exception)} method of the provided listener is invoked with an * {@link IllegalStateException}. If permit acquisition is delayed, the listener will be invoked on the executor with the specified * name. @@ -3522,7 +3526,7 @@ public void acquireReplicaOperationPermit( final long globalCheckpoint, final long maxSeqNoOfUpdatesOrDeletes, final ActionListener onPermitAcquired, - final String executorOnDelay + final Executor executorOnDelay ) { innerAcquireReplicaOperationPermit( opPrimaryTerm, @@ -4138,7 +4142,7 @@ public long getMaxSeqNoOfUpdatesOrDeletes() { * These transfers guarantee that every index/delete operation when executing on a replica engine will observe this marker a value * which is at least the value of the max_seq_no_of_updates marker on the primary after that operation was executed on the primary. * - * @see #acquireReplicaOperationPermit(long, long, long, ActionListener, String) + * @see #acquireReplicaOperationPermit(long, long, long, ActionListener, Executor) * @see RecoveryTarget#indexTranslogOperations(List, int, long, long, RetentionLeases, long, ActionListener) */ public void advanceMaxSeqNoOfUpdatesOrDeletes(long seqNo) { diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShardOperationPermits.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShardOperationPermits.java index 9c4cd3958936d..9da78953d7213 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShardOperationPermits.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShardOperationPermits.java @@ -18,6 +18,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext; import org.elasticsearch.core.Assertions; import org.elasticsearch.core.IOUtils; +import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; @@ -27,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -77,13 +79,18 @@ public void close() { * @param timeUnit the time unit of the {@code timeout} argument * @param executor executor on which to wait for in-flight operations to finish and acquire all permits */ - public void blockOperations(final ActionListener onAcquired, final long timeout, final TimeUnit timeUnit, String executor) { + public void blockOperations( + final ActionListener onAcquired, + final long timeout, + final TimeUnit timeUnit, + final Executor executor + ) { delayOperations(); waitUntilBlocked(ActionListener.assertOnce(onAcquired), timeout, timeUnit, executor); } - private void waitUntilBlocked(ActionListener onAcquired, long timeout, TimeUnit timeUnit, String executor) { - threadPool.executor(executor).execute(new AbstractRunnable() { + private void waitUntilBlocked(ActionListener onAcquired, long timeout, TimeUnit timeUnit, Executor executor) { + executor.execute(new AbstractRunnable() { final Releasable released = Releasables.releaseOnce(() -> releaseDelayedOperations()); @@ -187,11 +194,19 @@ private void releaseDelayedOperations() { * @param executorOnDelay executor to use for the possibly delayed {@link ActionListener#onResponse(Object)} call * @param forceExecution whether the runnable should force its execution in case it gets rejected */ - public void acquire(final ActionListener onAcquired, final String executorOnDelay, final boolean forceExecution) { + public void acquire( + final ActionListener onAcquired, + @Nullable final Executor executorOnDelay, + final boolean forceExecution + ) { innerAcquire(ActionListener.assertOnce(onAcquired), executorOnDelay, forceExecution); } - private void innerAcquire(final ActionListener onAcquired, final String executorOnDelay, final boolean forceExecution) { + private void innerAcquire( + final ActionListener onAcquired, + @Nullable final Executor executorOnDelay, + final boolean forceExecution + ) { if (closed) { onAcquired.onFailure(new IndexShardClosedException(shardId)); return; @@ -204,7 +219,7 @@ private void innerAcquire(final ActionListener onAcquired, final Str final ActionListener wrappedListener; if (executorOnDelay != null) { wrappedListener = new ContextPreservingActionListener<>(contextSupplier, onAcquired).delegateFailure( - (l, r) -> threadPool.executor(executorOnDelay).execute(new ActionRunnable<>(l) { + (l, r) -> executorOnDelay.execute(new ActionRunnable<>(l) { @Override public boolean isForceExecution() { return forceExecution; diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java index 1acc0f1041e39..fc5df1a4aa282 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java @@ -422,7 +422,7 @@ public void onFailure(Exception e) { cancellableThreads.checkForCancel(); ensureNotRelocatedPrimary(primary); action.accept(l2); - })), ThreadPool.Names.GENERIC); + })), primary.getThreadPool().generic()); } static void runUnderPrimaryPermit( diff --git a/server/src/test/java/org/elasticsearch/action/resync/TransportResyncReplicationActionTests.java b/server/src/test/java/org/elasticsearch/action/resync/TransportResyncReplicationActionTests.java index b41f0e667966c..f3cf4edfb8bec 100644 --- a/server/src/test/java/org/elasticsearch/action/resync/TransportResyncReplicationActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/resync/TransportResyncReplicationActionTests.java @@ -50,6 +50,7 @@ import java.util.Collections; import java.util.HashSet; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -62,7 +63,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -147,7 +148,7 @@ public void testResyncDoesNotBlockOnPrimaryAction() throws Exception { acquiredPermits.incrementAndGet(); callback.onResponse(acquiredPermits::decrementAndGet); return null; - }).when(indexShard).acquirePrimaryOperationPermit(anyActionListener(), anyString(), eq(true)); + }).when(indexShard).acquirePrimaryOperationPermit(anyActionListener(), any(Executor.class), eq(true)); when(indexShard.getReplicationGroup()).thenReturn( new ReplicationGroup( shardRoutingTable, diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index 09d22bd00f602..308c8a66767fa 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -91,6 +91,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -944,7 +945,7 @@ public void testSeqNoIsSetOnPrimary() { ActionListener argument = (ActionListener) invocation.getArguments()[0]; argument.onResponse(count::decrementAndGet); return null; - }).when(shard).acquirePrimaryOperationPermit(any(), anyString(), eq(forceExecute)); + }).when(shard).acquirePrimaryOperationPermit(any(), any(Executor.class), eq(forceExecute)); when(shard.getActiveOperationsCount()).thenAnswer(i -> count.get()); final IndexService indexService = mock(IndexService.class); @@ -1580,7 +1581,7 @@ private IndexShard mockIndexShard(ShardId shardId, ClusterService clusterService callback.onFailure(new ShardNotInPrimaryModeException(shardId, IndexShardState.STARTED)); } return null; - }).when(indexShard).acquirePrimaryOperationPermit(any(ActionListener.class), anyString(), eq(forceExecute)); + }).when(indexShard).acquirePrimaryOperationPermit(any(ActionListener.class), any(Executor.class), eq(forceExecute)); when(indexShard.isPrimaryMode()).thenAnswer(invocation -> isPrimaryMode.get()); doAnswer(invocation -> { long term = (Long) invocation.getArguments()[0]; @@ -1594,7 +1595,7 @@ private IndexShard mockIndexShard(ShardId shardId, ClusterService clusterService count.incrementAndGet(); callback.onResponse(count::decrementAndGet); return null; - }).when(indexShard).acquireReplicaOperationPermit(anyLong(), anyLong(), anyLong(), any(ActionListener.class), anyString()); + }).when(indexShard).acquireReplicaOperationPermit(anyLong(), anyLong(), anyLong(), any(ActionListener.class), any(Executor.class)); when(indexShard.getActiveOperationsCount()).thenAnswer(i -> count.get()); when(indexShard.routingEntry()).thenAnswer(invocationOnMock -> { diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java index 7726feaa30868..4f926b14a1904 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java @@ -61,6 +61,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -545,7 +546,7 @@ private IndexShard mockIndexShard(ShardId shardId, ClusterService clusterService count.incrementAndGet(); callback.onResponse(count::decrementAndGet); return null; - }).when(indexShard).acquirePrimaryOperationPermit(anyActionListener(), anyString(), any()); + }).when(indexShard).acquirePrimaryOperationPermit(anyActionListener(), any(Executor.class), any()); doAnswer(invocation -> { long term = (Long) invocation.getArguments()[0]; @SuppressWarnings("unchecked") @@ -559,7 +560,7 @@ private IndexShard mockIndexShard(ShardId shardId, ClusterService clusterService count.incrementAndGet(); callback.onResponse(count::decrementAndGet); return null; - }).when(indexShard).acquireReplicaOperationPermit(anyLong(), anyLong(), anyLong(), anyActionListener(), anyString()); + }).when(indexShard).acquireReplicaOperationPermit(anyLong(), anyLong(), anyLong(), anyActionListener(), any(Executor.class)); when(indexShard.routingEntry()).thenAnswer(invocationOnMock -> { final ClusterState state = clusterService.state(); final RoutingNode node = state.getRoutingNodes().node(state.nodes().getLocalNodeId()); diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardOperationPermitsTests.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardOperationPermitsTests.java index 240e0b9abd618..c6f0df53d68ff 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardOperationPermitsTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardOperationPermitsTests.java @@ -36,6 +36,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -110,7 +111,7 @@ public void testAllOperationsInvoked() throws InterruptedException, TimeoutExcep boolean forceExecution = randomBoolean(); for (int i = 0; i < numThreads; i++) { // the write thread pool uses a bounded size and can get rejections, see setupThreadPool - String threadPoolName = randomFrom(ThreadPool.Names.WRITE, ThreadPool.Names.GENERIC); + Executor executor = threadPool.executor(randomFrom(ThreadPool.Names.WRITE, ThreadPool.Names.GENERIC)); PlainActionFuture future = new PlainActionFuture() { @Override public void onResponse(Releasable releasable) { @@ -120,7 +121,7 @@ public void onResponse(Releasable releasable) { }; Thread thread = new Thread(() -> { latch.countDown(); - permits.acquire(future, threadPoolName, forceExecution); + permits.acquire(future, executor, forceExecution); }); futures.add(future); operationThreads.add(thread); @@ -169,7 +170,7 @@ public void onResponse(Releasable releasable) { public void testOperationsInvokedImmediatelyIfNoBlock() throws ExecutionException, InterruptedException { PlainActionFuture future = new PlainActionFuture<>(); - permits.acquire(future, ThreadPool.Names.GENERIC, true); + permits.acquire(future, threadPool.generic(), true); assertTrue(future.isDone()); future.get().close(); } @@ -177,7 +178,7 @@ public void testOperationsInvokedImmediatelyIfNoBlock() throws ExecutionExceptio public void testOperationsIfClosed() { PlainActionFuture future = new PlainActionFuture<>(); permits.close(); - permits.acquire(future, ThreadPool.Names.GENERIC, true); + permits.acquire(future, threadPool.generic(), true); ExecutionException exception = expectThrows(ExecutionException.class, future::get); assertThat(exception.getCause(), instanceOf(IndexShardClosedException.class)); } @@ -190,7 +191,7 @@ public void testBlockIfClosed() { wrap(() -> { throw new IllegalArgumentException("fake error"); }), randomInt(10), TimeUnit.MINUTES, - ThreadPool.Names.GENERIC + threadPool.generic() ) ); } @@ -198,7 +199,7 @@ public void testBlockIfClosed() { public void testOperationsDelayedIfBlock() throws ExecutionException, InterruptedException, TimeoutException { PlainActionFuture future = new PlainActionFuture<>(); try (Releasable ignored = blockAndWait()) { - permits.acquire(future, ThreadPool.Names.GENERIC, true); + permits.acquire(future, threadPool.generic(), true); assertFalse(future.isDone()); } future.get(1, TimeUnit.HOURS).close(); @@ -210,13 +211,13 @@ public void testGetBlockWhenBlocked() throws ExecutionException, InterruptedExce final CountDownLatch releaseBlock = new CountDownLatch(1); final AtomicBoolean blocked = new AtomicBoolean(); try (Releasable ignored = blockAndWait()) { - permits.acquire(future, ThreadPool.Names.GENERIC, true); + permits.acquire(future, threadPool.generic(), true); permits.blockOperations(wrap(() -> { blocked.set(true); blockAcquired.countDown(); releaseBlock.await(); - }), 30, TimeUnit.MINUTES, ThreadPool.Names.GENERIC); + }), 30, TimeUnit.MINUTES, threadPool.generic()); assertFalse(blocked.get()); assertFalse(future.isDone()); } @@ -275,7 +276,7 @@ public void onResponse(Releasable releasable) { context.putHeader("foo", "bar"); context.putTransient("bar", "baz"); // test both with and without a executor name - permits.acquire(future, ThreadPool.Names.GENERIC, true); + permits.acquire(future, threadPool.generic(), true); permits.acquire(future2, null, true); } assertFalse(future.isDone()); @@ -310,7 +311,7 @@ public void onFailure(Exception e) { throw new RuntimeException(e); } } - }, blockReleased::countDown), 1, TimeUnit.MINUTES, ThreadPool.Names.GENERIC); + }, blockReleased::countDown), 1, TimeUnit.MINUTES, threadPool.generic()); blockAcquired.await(); return () -> { releaseBlock.countDown(); @@ -330,7 +331,7 @@ public void testAsyncBlockOperationsOperationWhileBlocked() throws InterruptedEx blocked.set(true); blockAcquired.countDown(); releaseBlock.await(); - }), 30, TimeUnit.MINUTES, ThreadPool.Names.GENERIC); + }), 30, TimeUnit.MINUTES, threadPool.generic()); blockAcquired.await(); assertTrue(blocked.get()); @@ -349,7 +350,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { } - }, ThreadPool.Names.GENERIC, false)); + }, threadPool.generic(), false)); thread.start(); assertFalse(delayed.get()); releaseBlock.countDown(); @@ -378,7 +379,7 @@ public void testAsyncBlockOperationsOperationBeforeBlocked() throws InterruptedE permits.blockOperations(wrap(() -> { onBlocked.set(true); blockedLatch.countDown(); - }), 30, TimeUnit.MINUTES, ThreadPool.Names.GENERIC); + }), 30, TimeUnit.MINUTES, threadPool.generic()); assertFalse(onBlocked.get()); // if we submit another operation, it should be delayed @@ -399,7 +400,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { throw new RuntimeException(e); } - }, ThreadPool.Names.GENERIC, false); + }, threadPool.generic(), false); }); secondOperationThread.start(); @@ -445,7 +446,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { } - }, ThreadPool.Names.GENERIC, false); + }, threadPool.generic(), false); }); thread.start(); threads.add(thread); @@ -460,7 +461,7 @@ public void onFailure(Exception e) { permits.blockOperations(wrap(() -> { values.add(operations); operationLatch.countDown(); - }), 30, TimeUnit.MINUTES, ThreadPool.Names.GENERIC); + }), 30, TimeUnit.MINUTES, threadPool.generic()); }); blockingThread.start(); @@ -495,12 +496,12 @@ public void onFailure(Exception e) { public void testActiveOperationsCount() throws ExecutionException, InterruptedException { PlainActionFuture future1 = new PlainActionFuture<>(); - permits.acquire(future1, ThreadPool.Names.GENERIC, true); + permits.acquire(future1, threadPool.generic(), true); assertTrue(future1.isDone()); assertThat(permits.getActiveOperationsCount(), equalTo(1)); PlainActionFuture future2 = new PlainActionFuture<>(); - permits.acquire(future2, ThreadPool.Names.GENERIC, true); + permits.acquire(future2, threadPool.generic(), true); assertTrue(future2.isDone()); assertThat(permits.getActiveOperationsCount(), equalTo(2)); @@ -516,7 +517,7 @@ public void testActiveOperationsCount() throws ExecutionException, InterruptedEx } PlainActionFuture future3 = new PlainActionFuture<>(); - permits.acquire(future3, ThreadPool.Names.GENERIC, true); + permits.acquire(future3, threadPool.generic(), true); assertTrue(future3.isDone()); assertThat(permits.getActiveOperationsCount(), equalTo(1)); future3.get().close(); @@ -525,26 +526,21 @@ public void testActiveOperationsCount() throws ExecutionException, InterruptedEx public void testAsyncBlockOperationsOnRejection() { final PlainActionFuture threadBlock = new PlainActionFuture<>(); - try ( - Releasable firstPermit = PlainActionFuture.get(f -> permits.acquire(f, ThreadPool.Names.GENERIC, false), 0, TimeUnit.SECONDS) - ) { + try (Releasable firstPermit = PlainActionFuture.get(f -> permits.acquire(f, threadPool.generic(), false), 0, TimeUnit.SECONDS)) { assertNotNull(firstPermit); - threadPool.executor(REJECTING_EXECUTOR).execute(threadBlock::actionGet); + final var rejectingExecutor = threadPool.executor(REJECTING_EXECUTOR); + rejectingExecutor.execute(threadBlock::actionGet); expectThrows( EsRejectedExecutionException.class, () -> PlainActionFuture.get( - f -> permits.blockOperations(f, 1, TimeUnit.HOURS, REJECTING_EXECUTOR) + f -> permits.blockOperations(f, 1, TimeUnit.HOURS, rejectingExecutor) ) ); // ensure that the exception means no block was put in place try ( - Releasable secondPermit = PlainActionFuture.get( - f -> permits.acquire(f, ThreadPool.Names.GENERIC, false), - 0, - TimeUnit.SECONDS - ) + Releasable secondPermit = PlainActionFuture.get(f -> permits.acquire(f, threadPool.generic(), false), 0, TimeUnit.SECONDS) ) { assertNotNull(secondPermit); } @@ -553,16 +549,14 @@ public void testAsyncBlockOperationsOnRejection() { } // ensure that another block can still be acquired - try (Releasable block = PlainActionFuture.get(f -> permits.blockOperations(f, 1, TimeUnit.HOURS, ThreadPool.Names.GENERIC))) { + try (Releasable block = PlainActionFuture.get(f -> permits.blockOperations(f, 1, TimeUnit.HOURS, threadPool.generic()))) { assertNotNull(block); } } public void testAsyncBlockOperationsOnTimeout() { final PlainActionFuture threadBlock = new PlainActionFuture<>(); - try ( - Releasable firstPermit = PlainActionFuture.get(f -> permits.acquire(f, ThreadPool.Names.GENERIC, false), 0, TimeUnit.SECONDS) - ) { + try (Releasable firstPermit = PlainActionFuture.get(f -> permits.acquire(f, threadPool.generic(), false), 0, TimeUnit.SECONDS)) { assertNotNull(firstPermit); assertEquals( @@ -570,18 +564,14 @@ public void testAsyncBlockOperationsOnTimeout() { expectThrows( ElasticsearchTimeoutException.class, () -> PlainActionFuture.get( - f -> permits.blockOperations(f, 0, TimeUnit.SECONDS, ThreadPool.Names.GENERIC) + f -> permits.blockOperations(f, 0, TimeUnit.SECONDS, threadPool.generic()) ) ).getMessage() ); // ensure that the exception means no block was put in place try ( - Releasable secondPermit = PlainActionFuture.get( - f -> permits.acquire(f, ThreadPool.Names.GENERIC, false), - 0, - TimeUnit.SECONDS - ) + Releasable secondPermit = PlainActionFuture.get(f -> permits.acquire(f, threadPool.generic(), false), 0, TimeUnit.SECONDS) ) { assertNotNull(secondPermit); } @@ -591,7 +581,7 @@ public void testAsyncBlockOperationsOnTimeout() { } // ensure that another block can still be acquired - try (Releasable block = PlainActionFuture.get(f -> permits.blockOperations(f, 1, TimeUnit.HOURS, ThreadPool.Names.GENERIC))) { + try (Releasable block = PlainActionFuture.get(f -> permits.blockOperations(f, 1, TimeUnit.HOURS, threadPool.generic()))) { assertNotNull(block); } } @@ -622,7 +612,7 @@ public void onFailure(final Exception e) { reference.set(e); onFailureLatch.countDown(); } - }, 1, TimeUnit.MILLISECONDS, ThreadPool.Names.GENERIC); + }, 1, TimeUnit.MILLISECONDS, threadPool.generic()); onFailureLatch.await(); assertThat(reference.get(), hasToString(containsString("timeout while blocking operations"))); @@ -647,7 +637,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { assert false; } - }, ThreadPool.Names.GENERIC, false) + }, threadPool.generic(), false) ); assertThat(e, hasToString(containsString("failed to obtain permit but operations are not delayed"))); permits.semaphore.release(IndexShardOperationPermits.TOTAL_PERMITS); @@ -697,7 +687,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { throw new RuntimeException(e); } - }, ThreadPool.Names.GENERIC, false); + }, threadPool.generic(), false); }; } diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index 5fdfb92d3a193..4850cd4f4cf5e 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -60,6 +60,7 @@ import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ReleasableLock; import org.elasticsearch.core.Assertions; import org.elasticsearch.core.CheckedFunction; @@ -150,6 +151,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -330,7 +332,7 @@ public void testClosesPreventsNewOperations() throws Exception { IndexShard indexShard = newStartedShard(); closeShards(indexShard); assertThat(indexShard.getActiveOperationsCount(), equalTo(0)); - expectThrows(IndexShardClosedException.class, () -> indexShard.acquirePrimaryOperationPermit(null, ThreadPool.Names.WRITE)); + expectThrows(IndexShardClosedException.class, () -> indexShard.acquirePrimaryOperationPermit(null, writeExecutor)); expectThrows( IndexShardClosedException.class, () -> indexShard.acquireAllPrimaryOperationsPermits(null, TimeValue.timeValueSeconds(30L)) @@ -342,7 +344,7 @@ public void testClosesPreventsNewOperations() throws Exception { UNASSIGNED_SEQ_NO, randomNonNegativeLong(), null, - ThreadPool.Names.WRITE + writeExecutor ) ); expectThrows( @@ -364,7 +366,7 @@ public void testRunUnderPrimaryPermitRunsUnderPrimaryPermit() throws IOException indexShard.runUnderPrimaryPermit( () -> assertThat(indexShard.getActiveOperationsCount(), equalTo(1)), e -> fail(e.toString()), - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); assertThat(indexShard.getActiveOperationsCount(), equalTo(0)); } finally { @@ -380,7 +382,7 @@ public void testRunUnderPrimaryPermitOnFailure() throws IOException { assertThat(e, instanceOf(RuntimeException.class)); assertThat(e.getMessage(), equalTo("failure")); invoked.set(true); - }, ThreadPool.Names.SAME); + }, EsExecutors.DIRECT_EXECUTOR_SERVICE); assertTrue(invoked.get()); } finally { closeShards(indexShard); @@ -406,7 +408,7 @@ public void testRunUnderPrimaryPermitDelaysToExecutorWhenBlocked() throws Except : executorOnDelay.toLowerCase(Locale.ROOT); assertThat(Thread.currentThread().getName(), containsString(expectedThreadPoolName)); latch.countDown(); - }, e -> fail(e.toString()), executorOnDelay); + }, e -> fail(e.toString()), threadPool.executor(executorOnDelay)); permit.close(); latch.await(); // we could race and assert on the count before the permit is returned @@ -469,7 +471,7 @@ public void onFailure(Exception e) { throw new RuntimeException(e); } }, - ThreadPool.Names.WRITE + writeExecutor ); }); thread.start(); @@ -513,7 +515,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { throw new RuntimeException(e); } - }, ThreadPool.Names.WRITE); + }, writeExecutor); }); thread.start(); delayedThreads.add(thread); @@ -559,7 +561,7 @@ public void testPublishingOrderOnPromotion() throws IOException, InterruptedExce if (indexShard.routingEntry().primary()) { assertThat(indexShard.getPendingPrimaryTerm(), equalTo(promotedTerm)); final PlainActionFuture permitAcquiredFuture = new PlainActionFuture<>(); - indexShard.acquirePrimaryOperationPermit(permitAcquiredFuture, ThreadPool.Names.SAME); + indexShard.acquirePrimaryOperationPermit(permitAcquiredFuture, EsExecutors.DIRECT_EXECUTOR_SERVICE); try (Releasable ignored = permitAcquiredFuture.actionGet()) { assertThat(indexShard.getReplicationGroup(), notNullValue()); } @@ -619,7 +621,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { throw new AssertionError(e); } - }, ThreadPool.Names.GENERIC); + }, threadPool.generic()); latch.await(); assertThat(indexShard.getLocalCheckpoint(), equalTo((long) maxSeqNo)); @@ -667,7 +669,7 @@ public void onResponse(Releasable releasable) { public void onFailure(Exception e) { throw new RuntimeException(e); } - }, ThreadPool.Names.GENERIC); + }, threadPool.generic()); latch.await(); assertThat(getTranslog(indexShard).getGeneration().translogFileGeneration, equalTo(currentTranslogGeneration + 1)); @@ -750,7 +752,7 @@ public void onFailure(final Exception e) { assertThat(e, instanceOf(ShardNotInPrimaryModeException.class)); assertThat(e, hasToString(containsString("shard is not in primary mode"))); } - }, ThreadPool.Names.SAME); + }, EsExecutors.DIRECT_EXECUTOR_SERVICE); final CountDownLatch latch = new CountDownLatch(1); indexShard.acquireAllPrimaryOperationsPermits(new ActionListener<>() { @@ -788,7 +790,7 @@ public void onFailure(Exception e) { fail(); } }, - ThreadPool.Names.WRITE + writeExecutor ) ).getMessage(), containsString("in primary mode cannot be a replication target") @@ -842,7 +844,7 @@ public void onFailure(final Exception e) { throw new RuntimeException(e); } if (singlePermit) { - indexShard.acquirePrimaryOperationPermit(future, ThreadPool.Names.WRITE); + indexShard.acquirePrimaryOperationPermit(future, writeExecutor); } else { indexShard.acquireAllPrimaryOperationsPermits(future, TimeValue.timeValueHours(1L)); } @@ -899,7 +901,7 @@ public void onResponse(final Releasable releasable) { private Releasable acquirePrimaryOperationPermitBlockingly(IndexShard indexShard) throws ExecutionException, InterruptedException { PlainActionFuture fut = new PlainActionFuture<>(); - indexShard.acquirePrimaryOperationPermit(fut, ThreadPool.Names.WRITE); + indexShard.acquirePrimaryOperationPermit(fut, writeExecutor); return fut.get(); } @@ -911,7 +913,7 @@ private Releasable acquireReplicaOperationPermitBlockingly(IndexShard indexShard indexShard.getLastKnownGlobalCheckpoint(), randomNonNegativeLong(), fut, - ThreadPool.Names.WRITE + writeExecutor ); return fut.get(); } @@ -963,10 +965,7 @@ public void testOperationPermitOnReplicaShards() throws Exception { assertEquals(0, indexShard.getActiveOperationsCount()); if (shardRouting.primary() == false && Assertions.ENABLED) { - AssertionError e = expectThrows( - AssertionError.class, - () -> indexShard.acquirePrimaryOperationPermit(null, ThreadPool.Names.WRITE) - ); + AssertionError e = expectThrows(AssertionError.class, () -> indexShard.acquirePrimaryOperationPermit(null, writeExecutor)); assertThat(e, hasToString(containsString("acquirePrimaryOperationPermit should only be called on primary shard"))); e = expectThrows( @@ -1424,7 +1423,7 @@ public void onFailure(Exception e) { latch.countDown(); } }, - ThreadPool.Names.WRITE + writeExecutor ); }; @@ -1999,7 +1998,7 @@ public void onFailure(Exception e) { }); } - shard.acquirePrimaryOperationPermit(onLockAcquired, ThreadPool.Names.WRITE); + shard.acquirePrimaryOperationPermit(onLockAcquired, writeExecutor); } for (final Runnable assertion : assertions) { @@ -4539,7 +4538,7 @@ public void testTypelessGet() throws IOException { } /** - * Randomizes the usage of {@link IndexShard#acquireReplicaOperationPermit(long, long, long, ActionListener, String)} and + * Randomizes the usage of {@link IndexShard#acquireReplicaOperationPermit(long, long, long, ActionListener, Executor)} and * {@link IndexShard#acquireAllReplicaOperationsPermits(long, long, long, ActionListener, TimeValue)} in order to acquire a permit. */ private void randomReplicaOperationPermitAcquisition( @@ -4550,8 +4549,7 @@ private void randomReplicaOperationPermitAcquisition( final ActionListener listener ) { if (randomBoolean()) { - final String executor = ThreadPool.Names.WRITE; - indexShard.acquireReplicaOperationPermit(opPrimaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, listener, executor); + indexShard.acquireReplicaOperationPermit(opPrimaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, listener, writeExecutor); } else { final TimeValue timeout = TimeValue.timeValueSeconds(30L); indexShard.acquireAllReplicaOperationsPermits(opPrimaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, listener, timeout); diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java index d2f94ff2d344a..0285a8c66fffb 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java @@ -124,7 +124,6 @@ import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -709,6 +708,7 @@ public void testThrowExceptionOnPrimaryRelocatedBeforePhase1Started() throws IOE final RecoverySettings recoverySettings = new RecoverySettings(Settings.EMPTY, service); final StartRecoveryRequest request = getStartRecoveryRequest(); final IndexShard shard = mock(IndexShard.class); + when(shard.getThreadPool()).thenReturn(threadPool); when(shard.seqNoStats()).thenReturn(mock(SeqNoStats.class)); when(shard.segmentStats(anyBoolean(), anyBoolean())).thenReturn(mock(SegmentsStats.class)); when(shard.isRelocatedPrimary()).thenReturn(true); @@ -716,7 +716,7 @@ public void testThrowExceptionOnPrimaryRelocatedBeforePhase1Started() throws IOE doAnswer(invocation -> { ((ActionListener) invocation.getArguments()[0]).onResponse(() -> {}); return null; - }).when(shard).acquirePrimaryOperationPermit(any(), anyString()); + }).when(shard).acquirePrimaryOperationPermit(any(), any(Executor.class)); final IndexMetadata.Builder indexMetadata = IndexMetadata.builder("test") .settings( @@ -799,11 +799,12 @@ public void testCancellationsDoesNotLeakPrimaryPermits() throws Exception { final IndexShard shard = mock(IndexShard.class); final AtomicBoolean freed = new AtomicBoolean(true); when(shard.isRelocatedPrimary()).thenReturn(false); + when(shard.getThreadPool()).thenReturn(threadPool); doAnswer(invocation -> { freed.set(false); ((ActionListener) invocation.getArguments()[0]).onResponse(() -> freed.set(true)); return null; - }).when(shard).acquirePrimaryOperationPermit(any(), anyString()); + }).when(shard).acquirePrimaryOperationPermit(any(), any(Executor.class)); Thread cancelingThread = new Thread(() -> cancellableThreads.cancel("test")); cancelingThread.start(); diff --git a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index c2ce750f155cf..a05b2510bc056 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -52,6 +52,7 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; @@ -619,7 +620,7 @@ public void executeRetentionLeasesSyncRequestOnReplica(RetentionLeaseSyncAction. getPrimary().getLastKnownGlobalCheckpoint(), getPrimary().getMaxSeqNoOfUpdatesOrDeletes(), acquirePermitFuture, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); try (Releasable ignored = acquirePermitFuture.actionGet()) { replica.updateRetentionLeasesOnReplica(request.getRetentionLeases()); @@ -792,7 +793,7 @@ public void performOn( delegatedListener.onFailure(e); } }), - ThreadPool.Names.WRITE + replica.getThreadPool().executor(ThreadPool.Names.WRITE) ); } @@ -883,7 +884,7 @@ private void executeShardBulkOnPrimary( } } final PlainActionFuture permitAcquiredFuture = new PlainActionFuture<>(); - primary.acquirePrimaryOperationPermit(permitAcquiredFuture, ThreadPool.Names.SAME); + primary.acquirePrimaryOperationPermit(permitAcquiredFuture, EsExecutors.DIRECT_EXECUTOR_SERVICE); try (Releasable ignored = permitAcquiredFuture.actionGet()) { MappingUpdatePerformer noopMappingUpdater = (_update, _shardId, _listener1) -> {}; TransportShardBulkAction.performOnPrimary( @@ -938,7 +939,7 @@ private void executeShardBulkOnReplica( globalCheckpointOnPrimary, maxSeqNoOfUpdatesOrDeletes, permitAcquiredFuture, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); final Translog.Location location; try (Releasable ignored = permitAcquiredFuture.actionGet()) { @@ -1086,7 +1087,7 @@ private void executeResyncOnReplica( globalCheckpointOnPrimary, maxSeqNoOfUpdatesOrDeletes, acquirePermitFuture, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); try (Releasable ignored = acquirePermitFuture.actionGet()) { location = TransportResyncReplicationAction.performOnReplica(request, replica); diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index f36951cec0b84..beaa800b72588 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -91,6 +91,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -137,12 +138,14 @@ public void onRecoveryFailure(RecoveryFailedException e, boolean sendShardFailur }; protected ThreadPool threadPool; + protected Executor writeExecutor; protected long primaryTerm; @Override public void setUp() throws Exception { super.setUp(); threadPool = setUpThreadPool(); + writeExecutor = threadPool.executor(ThreadPool.Names.WRITE); primaryTerm = randomIntBetween(1, 100); // use random but fixed term for creating shards failOnShardFailures(); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java index 01168e447eeb9..b7d333de80a75 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Assertions; import org.elasticsearch.core.Releasable; import org.elasticsearch.index.Index; @@ -133,7 +134,7 @@ public void onFailure(Exception e) { onFailure(e); } } - }, ThreadPool.Names.SAME); + }, EsExecutors.DIRECT_EXECUTOR_SERVICE); } @Override diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java index e37bd879ec943..0d28f99a64237 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskReplicationTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; @@ -359,7 +360,7 @@ public void testRetryBulkShardOperations() throws Exception { followingPrimary.getLastKnownGlobalCheckpoint(), followingPrimary.getMaxSeqNoOfUpdatesOrDeletes(), permitFuture, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); try (Releasable ignored = permitFuture.get()) { TransportBulkShardOperationsAction.shardOperationOnReplica(primaryResult.replicaRequest(), replica, logger); @@ -802,7 +803,7 @@ class CcrAction extends ReplicationAction listener) { final PlainActionFuture permitFuture = new PlainActionFuture<>(); - primary.acquirePrimaryOperationPermit(permitFuture, ThreadPool.Names.SAME); + primary.acquirePrimaryOperationPermit(permitFuture, EsExecutors.DIRECT_EXECUTOR_SERVICE); final TransportWriteAction.WritePrimaryResult ccrResult; final var threadpool = mock(ThreadPool.class); final var transportService = mock(TransportService.class); @@ -838,7 +839,7 @@ protected void performOnReplica(BulkShardOperationsRequest request, IndexShard r getPrimaryShard().getLastKnownGlobalCheckpoint(), getPrimaryShard().getMaxSeqNoOfUpdatesOrDeletes(), f, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ) ) ) { diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/index/engine/FollowEngineIndexShardTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/index/engine/FollowEngineIndexShardTests.java index 2156995f2e0f6..c51b7c346272c 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/index/engine/FollowEngineIndexShardTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/index/engine/FollowEngineIndexShardTests.java @@ -34,7 +34,6 @@ import org.elasticsearch.repositories.IndexId; import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotId; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.ccr.CcrSettings; @@ -103,7 +102,7 @@ public void testDoNotFillGaps() throws Exception { releasable.close(); latch.countDown(); }); - indexShard.acquirePrimaryOperationPermit(actionListener, ThreadPool.Names.GENERIC); + indexShard.acquirePrimaryOperationPermit(actionListener, threadPool.generic()); latch.await(); assertThat(indexShard.getLocalCheckpoint(), equalTo(seqNoBeforeGap)); indexShard.refresh("test"); From 96679d28ac5af0b114219fb3ff029306ad976f01 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 21 Sep 2023 17:54:28 +0300 Subject: [PATCH 020/155] ESQL: Account for an exception being thrown when building a BytesRefArrayBlock (#99726) Account for an exception being thrown when building a BytesRefArrayBlock by registering an empty BytesRef in the BytesRefArray, rather than doing nothing --- docs/changelog/99726.yaml | 6 ++ .../gen/ConvertEvaluatorImplementer.java | 4 + .../compute/data/BytesRefBlock.java | 2 + .../compute/data/BytesRefBlockBuilder.java | 4 +- .../compute/data/X-Block.java.st | 4 + .../compute/data/X-BlockBuilder.java.st | 4 +- .../rest-api-spec/test/100_bug_fix.yml | 55 +++++++++++ .../convert/ToIPFromStringEvaluator.java | 1 + .../convert/ToStringFromBooleanEvaluator.java | 1 + .../ToStringFromDatetimeEvaluator.java | 1 + .../convert/ToStringFromDoubleEvaluator.java | 1 + .../convert/ToStringFromIPEvaluator.java | 1 + .../convert/ToStringFromIntEvaluator.java | 1 + .../convert/ToStringFromLongEvaluator.java | 1 + .../ToStringFromUnsignedLongEvaluator.java | 1 + .../convert/ToStringFromVersionEvaluator.java | 1 + .../convert/ToVersionFromStringEvaluator.java | 1 + .../function/AbstractFunctionTestCase.java | 7 +- .../expression/function/TestCaseSupplier.java | 2 +- .../function/scalar/convert/ToIPTests.java | 95 +++++++++++++++++++ 20 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 docs/changelog/99726.yaml create mode 100644 x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/100_bug_fix.yml create mode 100644 x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPTests.java diff --git a/docs/changelog/99726.yaml b/docs/changelog/99726.yaml new file mode 100644 index 0000000000000..23350fdb85bd0 --- /dev/null +++ b/docs/changelog/99726.yaml @@ -0,0 +1,6 @@ +pr: 99726 +summary: "ESQL: Account for an exception being thrown when building a `BytesRefArrayBlock`" +area: ES|QL +type: bug +issues: + - 99472 diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java index 256dd690a7fb6..af42d94c236f2 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java @@ -27,6 +27,7 @@ import static org.elasticsearch.compute.gen.Types.BLOCK; import static org.elasticsearch.compute.gen.Types.BYTES_REF; import static org.elasticsearch.compute.gen.Types.BYTES_REF_ARRAY; +import static org.elasticsearch.compute.gen.Types.BYTES_REF_BLOCK; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; import static org.elasticsearch.compute.gen.Types.SOURCE; import static org.elasticsearch.compute.gen.Types.VECTOR; @@ -166,6 +167,9 @@ private MethodSpec evalVector() { } builder.endControlFlow(); builder.addStatement("nullsMask.set(p)"); + if (resultType.equals(BYTES_REF)) { + builder.addStatement("values.append($T.NULL_VALUE)", BYTES_REF_BLOCK); + } } builder.endControlFlow(); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java index ef063ce7a80be..30cce3dbf0bad 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java @@ -20,6 +20,8 @@ */ public sealed interface BytesRefBlock extends Block permits FilterBytesRefBlock, BytesRefArrayBlock, BytesRefVectorBlock { + BytesRef NULL_VALUE = new BytesRef(); + /** * Retrieves the BytesRef value stored at the given value index. * diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java index aaed753cc20e1..ed80dcf28fb8e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java @@ -17,8 +17,6 @@ */ final class BytesRefBlockBuilder extends AbstractBlockBuilder implements BytesRefBlock.Builder { - private static final BytesRef NULL_VALUE = new BytesRef(); - private BytesRefArray values; BytesRefBlockBuilder(int estimatedSize) { @@ -69,7 +67,7 @@ public BytesRefBlockBuilder endPositionEntry() { @Override protected void writeNullValue() { - values.append(NULL_VALUE); + values.append(BytesRefBlock.NULL_VALUE); } /** diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st index b66ece387f6f0..9588cdbe9c353 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st @@ -23,6 +23,10 @@ import java.io.IOException; */ public sealed interface $Type$Block extends Block permits Filter$Type$Block, $Type$ArrayBlock, $Type$VectorBlock { +$if(BytesRef)$ + BytesRef NULL_VALUE = new BytesRef(); + +$endif$ /** * Retrieves the $type$ value stored at the given value index. * diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st index ff517b1c85533..fc407361c04ba 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st @@ -23,8 +23,6 @@ $endif$ final class $Type$BlockBuilder extends AbstractBlockBuilder implements $Type$Block.Builder { $if(BytesRef)$ - private static final BytesRef NULL_VALUE = new BytesRef(); - private BytesRefArray values; BytesRefBlockBuilder(int estimatedSize) { @@ -96,7 +94,7 @@ $endif$ $if(BytesRef)$ @Override protected void writeNullValue() { - values.append(NULL_VALUE); + values.append(BytesRefBlock.NULL_VALUE); } $endif$ diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/100_bug_fix.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/100_bug_fix.yml new file mode 100644 index 0000000000000..21871e661e971 --- /dev/null +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/100_bug_fix.yml @@ -0,0 +1,55 @@ +--- +setup: + - do: + bulk: + index: test + refresh: true + body: + - { "index": { } } + - { "emp_no": 10, "ip1": "127.0", "ip2": "0.1" } + - { "index": { } } + - { "emp_no": 20 } + +--- +"Bug fix https://github.com/elastic/elasticsearch/issues/99472": + - skip: + features: warnings + - do: + warnings: + - "Line 1:37: evaluation of [to_ip(coalesce(ip1.keyword, \"255.255.255.255\"))] failed, treating result as null. Only first 20 failures recorded." + - "java.lang.IllegalArgumentException: '127.0' is not an IP string literal." + esql.query: + body: + query: 'FROM test | sort emp_no | eval ip = to_ip(coalesce(ip1.keyword, "255.255.255.255")) | keep emp_no, ip' + + - match: { columns.0.name: "emp_no" } + - match: { columns.0.type: "long" } + - match: { columns.1.name: "ip" } + - match: { columns.1.type: "ip" } + + - length: { values: 2 } + - match: { values.0: [ 10, null ] } + - match: { values.1: [ 20, "255.255.255.255"] } + + + - do: + warnings: + - "Line 1:98: evaluation of [to_ip(x2)] failed, treating result as null. Only first 20 failures recorded." + - "java.lang.IllegalArgumentException: '127.00.1' is not an IP string literal." + esql.query: + body: + query: 'FROM test | sort emp_no | eval x1 = concat(ip1, ip2), x2 = coalesce(x1, "255.255.255.255"), x3 = to_ip(x2) | keep emp_no, x*' + + - match: { columns.0.name: "emp_no" } + - match: { columns.0.type: "long" } + - match: { columns.1.name: "x1" } + - match: { columns.1.type: "keyword" } + - match: { columns.2.name: "x2" } + - match: { columns.2.type: "keyword" } + - match: { columns.3.name: "x3" } + - match: { columns.3.type: "ip" } + + + - length: { values: 2 } + - match: { values.0: [ 10, "127.00.1", "127.00.1", null ] } + - match: { values.1: [ 20, null, "255.255.255.255", "255.255.255.255"] } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java index 7befa47c6009c..76d5c58961970 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java index c95d64ee0f8f5..876344b1c35bc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java index d5f7454fc0d12..8aa5148b21de4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java index deb897a4cf170..8c7994a3c0a68 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java index dbb54eb4dbb59..4e0249939cc91 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java index 6c4969948dbf0..d076b38f49b91 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java index d8fd6b0ebd57d..90448cb992cd2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java index f1a78b375ce7e..91e31c9626b5e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java index 7f3361587a38c..281b881bd6141 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java index d8136b4ec4127..5f6b62e16de52 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java @@ -58,6 +58,7 @@ public Block evalVector(Vector v) { nullsMask = new BitSet(positionCount); } nullsMask.set(p); + values.append(BytesRefBlock.NULL_VALUE); } } return nullsMask == null diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index d7c962ae15a20..5efd165b53173 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -327,7 +327,7 @@ protected static List anyNullIsNull(boolean entirelyNullPreser oc.evaluatorToString, oc.expectedType, nullValue(), - oc.getExpectedWarnings(), + null, oc.getExpectedTypeError() ); })); @@ -352,7 +352,7 @@ protected static List anyNullIsNull(boolean entirelyNullPreser "LiteralsEvaluator[block=null]", entirelyNullPreservesType == false && oc.getData().size() == 1 ? DataTypes.NULL : oc.expectedType, nullValue(), - oc.getExpectedWarnings(), + null, oc.getExpectedTypeError() ); })); @@ -475,7 +475,8 @@ private static String typeErrorMessage(List> validPerPosition, Lis Map.entry(Set.of(DataTypes.DOUBLE, DataTypes.NULL), "double"), Map.entry(Set.of(DataTypes.INTEGER, DataTypes.NULL), "integer"), Map.entry(Set.of(DataTypes.LONG, DataTypes.INTEGER, DataTypes.UNSIGNED_LONG, DataTypes.DOUBLE, DataTypes.NULL), "numeric"), - Map.entry(Set.of(DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.VERSION, DataTypes.NULL), "keyword, text or version") + Map.entry(Set.of(DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.VERSION, DataTypes.NULL), "keyword, text or version"), + Map.entry(Set.of(DataTypes.IP, DataTypes.KEYWORD, DataTypes.NULL), "ip or keyword") ); private static String expectedType(Set validTypes) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java index 073f82847e87a..626e4af81bf44 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java @@ -633,7 +633,7 @@ private static List ipCases() { ); } - private static List stringCases(DataType type) { + public static List stringCases(DataType type) { List result = new ArrayList<>(); result.add(new TypedDataSupplier("", () -> new BytesRef(""), type)); result.add( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPTests.java new file mode 100644 index 0000000000000..7f764d9de20dc --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPTests.java @@ -0,0 +1,95 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.convert; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.network.NetworkAddress; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.elasticsearch.xpack.ql.expression.Expression; +import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; +import org.elasticsearch.xpack.ql.type.DataTypes; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.stringCases; +import static org.elasticsearch.xpack.ql.util.StringUtils.parseIP; +import static org.hamcrest.Matchers.equalTo; + +public class ToIPTests extends AbstractFunctionTestCase { + public ToIPTests(@Name("TestCase") Supplier testCaseSupplier) { + this.testCase = testCaseSupplier.get(); + } + + @ParametersFactory + public static Iterable parameters() { + String read = "Attribute[channel=0]"; + String stringEvaluator = "ToIPFromStringEvaluator[field=" + read + "]"; + List suppliers = new ArrayList<>(); + + // convert from IP to IP + TestCaseSupplier.forUnaryIp(suppliers, read, DataTypes.IP, v -> v, List.of()); + + // convert any kind of string to IP, with warnings. + for (TestCaseSupplier.TypedDataSupplier supplier : stringCases(DataTypes.KEYWORD)) { + suppliers.add(new TestCaseSupplier(supplier.name(), List.of(supplier.type()), () -> { + BytesRef value = (BytesRef) supplier.supplier().get(); + TestCaseSupplier.TypedData typed = new TestCaseSupplier.TypedData(value, supplier.type(), "value"); + TestCaseSupplier.TestCase testCase = new TestCaseSupplier.TestCase( + List.of(typed), + stringEvaluator, + DataTypes.IP, + equalTo(null) + ).withWarning("Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.") + .withWarning("java.lang.IllegalArgumentException: '" + value.utf8ToString() + "' is not an IP string literal."); + return testCase; + })); + } + + // convert valid IPs shaped as strings + DataType inputType = DataTypes.KEYWORD; + for (TestCaseSupplier.TypedDataSupplier ipGen : validIPsAsStrings()) { + suppliers.add(new TestCaseSupplier(ipGen.name(), List.of(inputType), () -> { + BytesRef ip = (BytesRef) ipGen.supplier().get(); + TestCaseSupplier.TypedData typed = new TestCaseSupplier.TypedData(ip, inputType, "value"); + return new TestCaseSupplier.TestCase(List.of(typed), stringEvaluator, DataTypes.IP, equalTo(parseIP(ip.utf8ToString()))); + })); + } + + // add null as parameter + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers))); + } + + @Override + protected Expression build(Source source, List args) { + return new ToIP(source, args.get(0)); + } + + private static List validIPsAsStrings() { + return List.of( + new TestCaseSupplier.TypedDataSupplier("<127.0.0.1 ip>", () -> new BytesRef("127.0.0.1"), DataTypes.KEYWORD), + new TestCaseSupplier.TypedDataSupplier( + "", + () -> new BytesRef(NetworkAddress.format(ESTestCase.randomIp(true))), + DataTypes.KEYWORD + ), + new TestCaseSupplier.TypedDataSupplier( + "", + () -> new BytesRef(NetworkAddress.format(ESTestCase.randomIp(false))), + DataTypes.KEYWORD + ) + ); + } +} From eb761287e14c157dfbfb45a4801a62f0afb4953b Mon Sep 17 00:00:00 2001 From: Artem Prigoda Date: Thu, 21 Sep 2023 17:13:36 +0200 Subject: [PATCH 021/155] Mute RotatableSecretTests#testBasicRotation (#99761) See https://github.com/elastic/elasticsearch/issues/99759 --- .../org/elasticsearch/common/settings/RotatableSecretTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/elasticsearch/common/settings/RotatableSecretTests.java b/server/src/test/java/org/elasticsearch/common/settings/RotatableSecretTests.java index cf4d1c3d4f204..8fe845fb3b3db 100644 --- a/server/src/test/java/org/elasticsearch/common/settings/RotatableSecretTests.java +++ b/server/src/test/java/org/elasticsearch/common/settings/RotatableSecretTests.java @@ -27,6 +27,7 @@ public class RotatableSecretTests extends ESTestCase { private final SecureString secret2 = new SecureString(randomAlphaOfLength(10)); private final SecureString secret3 = new SecureString(randomAlphaOfLength(10)); + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99759") public void testBasicRotation() throws Exception { // initial state RotatableSecret rotatableSecret = new RotatableSecret(secret1); From 3f32affbb693a9bd3a120506b3c3d3b23d62f6ed Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 21 Sep 2023 17:08:43 +0100 Subject: [PATCH 022/155] Add component info versions to node info in a pluggable way (#99631) This adds a `ComponentVersionNumber` service interface for modules to provide version numbers for individual components to be reported inside node info. Initial implementations for `MlConfigVersion` and `TransformConfigVersion` are provided. --- docs/changelog/99631.yaml | 5 +++ docs/reference/cluster/nodes-info.asciidoc | 13 +++++++ .../nodesinfo/SimpleNodesInfoIT.java | 20 +++++----- server/src/main/java/module-info.java | 1 + .../org/elasticsearch/TransportVersions.java | 1 + .../node/info/ComponentVersionNumber.java | 26 +++++++++++++ .../admin/cluster/node/info/NodeInfo.java | 18 +++++++++ .../cluster/node/info/NodesInfoResponse.java | 5 +++ .../org/elasticsearch/node/NodeService.java | 20 ++++++++++ .../cluster/node/info/NodeInfoTests.java | 3 ++ .../remote/RemoteClusterNodesActionTests.java | 3 ++ .../cluster/stats/ClusterStatsNodesTests.java | 1 + .../ingest/ReservedPipelineActionTests.java | 1 + .../TransportVersionsFixupListenerTests.java | 1 + .../nodesinfo/NodeInfoStreamingTests.java | 6 +++ .../action/cat/RestPluginsActionTests.java | 2 + .../AutoscalingNodesInfoServiceTests.java | 2 + .../ComponentVersionsNodesInfoIT.java | 39 +++++++++++++++++++ .../core/src/main/java/module-info.java | 5 +++ .../core/ml/MlConfigVersionComponent.java | 23 +++++++++++ .../TransformConfigVersionComponent.java | 23 +++++++++++ ...n.cluster.node.info.ComponentVersionNumber | 2 + .../TransportNodeEnrollmentActionTests.java | 1 + ...InternalEnrollmentTokenGeneratorTests.java | 3 ++ 24 files changed, 214 insertions(+), 10 deletions(-) create mode 100644 docs/changelog/99631.yaml create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/ComponentVersionNumber.java create mode 100644 x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/nodesinfo/ComponentVersionsNodesInfoIT.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersionComponent.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionComponent.java create mode 100644 x-pack/plugin/core/src/main/resources/META-INF/services/org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber diff --git a/docs/changelog/99631.yaml b/docs/changelog/99631.yaml new file mode 100644 index 0000000000000..d9174de76f1ea --- /dev/null +++ b/docs/changelog/99631.yaml @@ -0,0 +1,5 @@ +pr: 99631 +summary: Add component info versions to node info in a pluggable way +area: Infra/REST API +type: enhancement +issues: [] diff --git a/docs/reference/cluster/nodes-info.asciidoc b/docs/reference/cluster/nodes-info.asciidoc index 00650ce948b6f..16da5a25d1fbf 100644 --- a/docs/reference/cluster/nodes-info.asciidoc +++ b/docs/reference/cluster/nodes-info.asciidoc @@ -139,6 +139,9 @@ include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=node-id] `index_version`:: The most recent index version that this node can read. +`component_versions`:: + The version numbers of individual components loaded in this node. + The `os` flag can be set to retrieve information that concern the operating system: @@ -236,6 +239,10 @@ The API returns the following response: "version": "{version}", "transport_version": 100000298, "index_version": 100000074, + "component_versions": { + "ml_config_version": 100000162, + "transform_config_version": 100000096 + }, "build_flavor": "default", "build_type": "{build_type}", "build_hash": "587409e", @@ -276,6 +283,7 @@ The API returns the following response: // TESTRESPONSE[s/"ip": "192.168.17"/"ip": $body.$_path/] // TESTRESPONSE[s/"transport_version": 100000298/"transport_version": $body.$_path/] // TESTRESPONSE[s/"index_version": 100000074/"index_version": $body.$_path/] +// TESTRESPONSE[s/"component_versions": \{[^\}]*\}/"component_versions": $body.$_path/] // TESTRESPONSE[s/"build_hash": "587409e"/"build_hash": $body.$_path/] // TESTRESPONSE[s/"roles": \[[^\]]*\]/"roles": $body.$_path/] // TESTRESPONSE[s/"attributes": \{[^\}]*\}/"attributes": $body.$_path/] @@ -311,6 +319,10 @@ The API returns the following response: "version": "{version}", "transport_version": 100000298, "index_version": 100000074, + "component_versions": { + "ml_config_version": 100000162, + "transform_config_version": 100000096 + }, "build_flavor": "default", "build_type": "{build_type}", "build_hash": "587409e", @@ -375,6 +387,7 @@ The API returns the following response: // TESTRESPONSE[s/"ip": "192.168.17"/"ip": $body.$_path/] // TESTRESPONSE[s/"transport_version": 100000298/"transport_version": $body.$_path/] // TESTRESPONSE[s/"index_version": 100000074/"index_version": $body.$_path/] +// TESTRESPONSE[s/"component_versions": \{[^\}]*\}/"component_versions": $body.$_path/] // TESTRESPONSE[s/"build_hash": "587409e"/"build_hash": $body.$_path/] // TESTRESPONSE[s/"roles": \[[^\]]*\]/"roles": $body.$_path/] // TESTRESPONSE[s/"attributes": \{[^\}]*\}/"attributes": $body.$_path/] diff --git a/server/src/internalClusterTest/java/org/elasticsearch/nodesinfo/SimpleNodesInfoIT.java b/server/src/internalClusterTest/java/org/elasticsearch/nodesinfo/SimpleNodesInfoIT.java index 3b0829587fdf1..15225edc47a60 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/nodesinfo/SimpleNodesInfoIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/nodesinfo/SimpleNodesInfoIT.java @@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; @ClusterScope(scope = Scope.TEST, numDataNodes = 0) @@ -42,29 +42,29 @@ public void testNodesInfos() throws Exception { logger.info("--> started nodes: {} and {}", server1NodeId, server2NodeId); NodesInfoResponse response = clusterAdmin().prepareNodesInfo().execute().actionGet(); - assertThat(response.getNodes().size(), is(2)); + assertThat(response.getNodes(), hasSize(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = clusterAdmin().nodesInfo(new NodesInfoRequest()).actionGet(); - assertThat(response.getNodes().size(), is(2)); + assertThat(response.getNodes(), hasSize(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = clusterAdmin().nodesInfo(new NodesInfoRequest(server1NodeId)).actionGet(); - assertThat(response.getNodes().size(), is(1)); + assertThat(response.getNodes(), hasSize(1)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); response = clusterAdmin().nodesInfo(new NodesInfoRequest(server1NodeId)).actionGet(); - assertThat(response.getNodes().size(), is(1)); + assertThat(response.getNodes(), hasSize(1)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); response = clusterAdmin().nodesInfo(new NodesInfoRequest(server2NodeId)).actionGet(); - assertThat(response.getNodes().size(), is(1)); + assertThat(response.getNodes(), hasSize(1)); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = clusterAdmin().nodesInfo(new NodesInfoRequest(server2NodeId)).actionGet(); - assertThat(response.getNodes().size(), is(1)); + assertThat(response.getNodes(), hasSize(1)); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); } @@ -81,7 +81,7 @@ public void testNodesInfosTotalIndexingBuffer() throws Exception { logger.info("--> started nodes: {} and {}", server1NodeId, server2NodeId); NodesInfoResponse response = clusterAdmin().prepareNodesInfo().execute().actionGet(); - assertThat(response.getNodes().size(), is(2)); + assertThat(response.getNodes(), hasSize(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertNotNull(response.getNodesMap().get(server1NodeId).getTotalIndexingBuffer()); assertThat(response.getNodesMap().get(server1NodeId).getTotalIndexingBuffer().getBytes(), greaterThan(0L)); @@ -92,7 +92,7 @@ public void testNodesInfosTotalIndexingBuffer() throws Exception { // again, using only the indices flag response = clusterAdmin().prepareNodesInfo().clear().setIndices(true).execute().actionGet(); - assertThat(response.getNodes().size(), is(2)); + assertThat(response.getNodes(), hasSize(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertNotNull(response.getNodesMap().get(server1NodeId).getTotalIndexingBuffer()); assertThat(response.getNodesMap().get(server1NodeId).getTotalIndexingBuffer().getBytes(), greaterThan(0L)); @@ -120,7 +120,7 @@ public void testAllocatedProcessors() throws Exception { NodesInfoResponse response = clusterAdmin().prepareNodesInfo().execute().actionGet(); - assertThat(response.getNodes().size(), is(2)); + assertThat(response.getNodes(), hasSize(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index c6e6a207ae6c0..19fe187eaa080 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -399,6 +399,7 @@ uses org.elasticsearch.internal.BuildExtension; uses org.elasticsearch.plugins.internal.SettingsExtension; uses RestExtension; + uses org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber; provides org.apache.lucene.codecs.PostingsFormat with diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 1fddeaf5d837e..926cc4801dddc 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -144,6 +144,7 @@ static TransportVersion def(int id) { public static final TransportVersion COMMIT_PRIMARY_TERM_GENERATION = def(8_501_00_1); public static final TransportVersion WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED = def(8_502_00_0); public static final TransportVersion RECOVERY_COMMIT_TOO_NEW_EXCEPTION_ADDED = def(8_503_00_0); + public static final TransportVersion NODE_INFO_COMPONENT_VERSIONS_ADDED = def(8_504_00_0); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/ComponentVersionNumber.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/ComponentVersionNumber.java new file mode 100644 index 0000000000000..3e2572410a172 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/ComponentVersionNumber.java @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.cluster.node.info; + +import org.elasticsearch.common.VersionId; + +/** + * Represents a version number of a subsidiary component to be reported in node info + */ +public interface ComponentVersionNumber { + /** + * Returns the component id to report this number under. This should be in snake_case. + */ + String componentId(); + + /** + * Returns the version id to report. + */ + VersionId versionNumber(); +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfo.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfo.java index 3086f22fae8bc..3156b9d3bac42 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfo.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfo.java @@ -43,6 +43,7 @@ public class NodeInfo extends BaseNodeResponse { private final Version version; private final TransportVersion transportVersion; private final IndexVersion indexVersion; + private final Map componentVersions; private final Build build; @Nullable @@ -71,6 +72,11 @@ public NodeInfo(StreamInput in) throws IOException { } else { indexVersion = IndexVersion.fromId(version.id); } + if (in.getTransportVersion().onOrAfter(TransportVersions.NODE_INFO_COMPONENT_VERSIONS_ADDED)) { + componentVersions = in.readImmutableMap(StreamInput::readString, StreamInput::readVInt); + } else { + componentVersions = Map.of(); + } build = Build.readBuild(in); if (in.readBoolean()) { totalIndexingBuffer = ByteSizeValue.ofBytes(in.readLong()); @@ -102,6 +108,7 @@ public NodeInfo( Version version, TransportVersion transportVersion, IndexVersion indexVersion, + Map componentVersions, Build build, DiscoveryNode node, @Nullable Settings settings, @@ -121,6 +128,7 @@ public NodeInfo( this.version = version; this.transportVersion = transportVersion; this.indexVersion = indexVersion; + this.componentVersions = componentVersions; this.build = build; this.settings = settings; addInfoIfNonNull(OsInfo.class, os); @@ -165,6 +173,13 @@ public IndexVersion getIndexVersion() { return indexVersion; } + /** + * The version numbers of other installed components + */ + public Map getComponentVersions() { + return componentVersions; + } + /** * The build version of the node. */ @@ -219,6 +234,9 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().onOrAfter(TransportVersions.NODE_INFO_INDEX_VERSION_ADDED)) { IndexVersion.writeVersion(indexVersion, out); } + if (out.getTransportVersion().onOrAfter(TransportVersions.NODE_INFO_COMPONENT_VERSIONS_ADDED)) { + out.writeMap(componentVersions, StreamOutput::writeString, StreamOutput::writeVInt); + } Build.writeBuild(build, out); if (totalIndexingBuffer == null) { out.writeBoolean(false); diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoResponse.java index 68769af5a17d9..4fa99db192db5 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoResponse.java @@ -66,6 +66,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("version", nodeInfo.getVersion()); builder.field("transport_version", nodeInfo.getTransportVersion().id()); builder.field("index_version", nodeInfo.getIndexVersion().id()); + builder.startObject("component_versions"); + for (var cv : nodeInfo.getComponentVersions().entrySet()) { + builder.field(cv.getKey(), cv.getValue()); + } + builder.endObject(); builder.field("build_flavor", nodeInfo.getBuild().flavor()); builder.field("build_type", nodeInfo.getBuild().type().displayName()); builder.field("build_hash", nodeInfo.getBuild().hash()); diff --git a/server/src/main/java/org/elasticsearch/node/NodeService.java b/server/src/main/java/org/elasticsearch/node/NodeService.java index f71fada3c046a..7cc9b67a53959 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeService.java +++ b/server/src/main/java/org/elasticsearch/node/NodeService.java @@ -11,6 +11,7 @@ import org.elasticsearch.Build; import org.elasticsearch.TransportVersion; import org.elasticsearch.Version; +import org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags; @@ -19,6 +20,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.core.Assertions; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Nullable; import org.elasticsearch.http.HttpServerTransport; @@ -37,7 +39,10 @@ import java.io.Closeable; import java.io.IOException; +import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public class NodeService implements Closeable { private final Settings settings; @@ -116,6 +121,7 @@ public NodeInfo info( Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + findComponentVersions(), Build.current(), transportService.getLocalNode(), settings ? settingsFilter.filter(this.settings) : null, @@ -133,6 +139,20 @@ public NodeInfo info( ); } + private Map findComponentVersions() { + var versions = pluginService.loadServiceProviders(ComponentVersionNumber.class) + .stream() + .collect(Collectors.toUnmodifiableMap(ComponentVersionNumber::componentId, cvn -> cvn.versionNumber().id())); + + if (Assertions.ENABLED) { + var isSnakeCase = Pattern.compile("[a-z_]+").asMatchPredicate(); + for (String key : versions.keySet()) { + assert isSnakeCase.test(key) : "Version component " + key + " should use snake_case"; + } + } + return versions; + } + public NodeStats stats( CommonStatsFlags indices, boolean os, diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfoTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfoTests.java index 35f17c24b3e89..3a14bbe10a56c 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfoTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodeInfoTests.java @@ -19,6 +19,8 @@ import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.index.IndexVersionUtils; +import java.util.Map; + import static java.util.Collections.emptySet; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -39,6 +41,7 @@ public void testGetInfo() { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), Build.current(), DiscoveryNodeUtils.builder("test_node") .roles(emptySet()) diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java index 863a57a42c2bc..7c6c1ffdcf98b 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -80,6 +81,7 @@ public void testDoExecuteForRemoteServerNodes() { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, node, null, @@ -150,6 +152,7 @@ public void testDoExecuteForRemoteNodes() { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, node, null, diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodesTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodesTests.java index 80c320493f4ac..f2b1cb28ce8b6 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodesTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/stats/ClusterStatsNodesTests.java @@ -325,6 +325,7 @@ private static NodeInfo createNodeInfo(String nodeId, String transportType, Stri Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), Build.current(), DiscoveryNodeUtils.create(nodeId, buildNewFakeTransportAddress()), settings.build(), diff --git a/server/src/test/java/org/elasticsearch/action/ingest/ReservedPipelineActionTests.java b/server/src/test/java/org/elasticsearch/action/ingest/ReservedPipelineActionTests.java index 05b35f992564f..e02f5f66e6c80 100644 --- a/server/src/test/java/org/elasticsearch/action/ingest/ReservedPipelineActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/ingest/ReservedPipelineActionTests.java @@ -103,6 +103,7 @@ public void setup() { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), Build.current(), discoveryNode, Settings.EMPTY, diff --git a/server/src/test/java/org/elasticsearch/cluster/service/TransportVersionsFixupListenerTests.java b/server/src/test/java/org/elasticsearch/cluster/service/TransportVersionsFixupListenerTests.java index 323c50bf23c3b..b7792c5f85207 100644 --- a/server/src/test/java/org/elasticsearch/cluster/service/TransportVersionsFixupListenerTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/service/TransportVersionsFixupListenerTests.java @@ -87,6 +87,7 @@ private static NodesInfoResponse getResponse(Map respo e.getValue(), null, null, + null, DiscoveryNodeUtils.create(e.getKey(), new TransportAddress(TransportAddress.META_ADDRESS, 9200)), null, null, diff --git a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java index 8a338333065d7..c92c9c5305777 100644 --- a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java +++ b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java @@ -49,6 +49,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static java.util.Collections.emptySet; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; @@ -107,6 +109,9 @@ private void compareJsonOutput(ToXContent param1, ToXContent param2) throws IOEx } private static NodeInfo createNodeInfo() { + Map componentVersions = IntStream.range(0, randomInt(5)) + .boxed() + .collect(Collectors.toUnmodifiableMap(i -> randomAlphaOfLength(10), i -> randomInt(Integer.MAX_VALUE))); Build build = Build.current(); DiscoveryNode node = DiscoveryNodeUtils.builder("test_node") .roles(emptySet()) @@ -233,6 +238,7 @@ private static NodeInfo createNodeInfo() { VersionUtils.randomVersion(random()), TransportVersionUtils.randomVersion(random()), IndexVersionUtils.randomVersion(random()), + componentVersions, build, node, settings, diff --git a/server/src/test/java/org/elasticsearch/rest/action/cat/RestPluginsActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/cat/RestPluginsActionTests.java index 5249fe6aec9f0..e58127e9a6d9b 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/cat/RestPluginsActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/cat/RestPluginsActionTests.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import static org.hamcrest.Matchers.empty; @@ -66,6 +67,7 @@ private Table buildTable(List pluginDescriptor) { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, node(i), null, diff --git a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodesInfoServiceTests.java b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodesInfoServiceTests.java index 673b05c7c2466..d9c3e813d3159 100644 --- a/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodesInfoServiceTests.java +++ b/x-pack/plugin/autoscaling/src/test/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodesInfoServiceTests.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionException; @@ -452,6 +453,7 @@ private static org.elasticsearch.action.admin.cluster.node.info.NodeInfo infoFor Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), Build.current(), node, null, diff --git a/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/nodesinfo/ComponentVersionsNodesInfoIT.java b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/nodesinfo/ComponentVersionsNodesInfoIT.java new file mode 100644 index 0000000000000..e5131fa482b7c --- /dev/null +++ b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/nodesinfo/ComponentVersionsNodesInfoIT.java @@ -0,0 +1,39 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.nodesinfo; + +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.test.ESIntegTestCase; + +import java.util.List; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.notNullValue; + +public class ComponentVersionsNodesInfoIT extends ESIntegTestCase { + + public void testNodesInfoComponentVersions() { + List nodesIds = internalCluster().startNodes(1); + final String node_1 = nodesIds.get(0); + + ClusterHealthResponse clusterHealth = clusterAdmin().prepareHealth().setWaitForGreenStatus().setWaitForNodes("1").get(); + logger.info("--> done cluster_health, status {}", clusterHealth.getStatus()); + + String server1NodeId = internalCluster().getInstance(ClusterService.class, node_1).state().nodes().getLocalNodeId(); + logger.info("--> started nodes: {}", server1NodeId); + + NodesInfoResponse response = clusterAdmin().prepareNodesInfo().execute().actionGet(); + assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); + assertThat( + response.getNodesMap().get(server1NodeId).getComponentVersions().keySet(), + containsInAnyOrder("transform_config_version", "ml_config_version") + ); + } +} diff --git a/x-pack/plugin/core/src/main/java/module-info.java b/x-pack/plugin/core/src/main/java/module-info.java index 3a869aca80c4f..deb3c4384a04b 100644 --- a/x-pack/plugin/core/src/main/java/module-info.java +++ b/x-pack/plugin/core/src/main/java/module-info.java @@ -221,4 +221,9 @@ exports org.elasticsearch.xpack.core.watcher.trigger; exports org.elasticsearch.xpack.core.watcher.watch; exports org.elasticsearch.xpack.core.watcher; + + provides org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber + with + org.elasticsearch.xpack.core.ml.MlConfigVersionComponent, + org.elasticsearch.xpack.core.transform.TransformConfigVersionComponent; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersionComponent.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersionComponent.java new file mode 100644 index 0000000000000..d100788418be8 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersionComponent.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.ml; + +import org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber; +import org.elasticsearch.common.VersionId; + +public class MlConfigVersionComponent implements ComponentVersionNumber { + @Override + public String componentId() { + return "ml_config_version"; + } + + @Override + public VersionId versionNumber() { + return MlConfigVersion.CURRENT; + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionComponent.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionComponent.java new file mode 100644 index 0000000000000..e96056fa6fd7e --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionComponent.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.transform; + +import org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber; +import org.elasticsearch.common.VersionId; + +public class TransformConfigVersionComponent implements ComponentVersionNumber { + @Override + public String componentId() { + return "transform_config_version"; + } + + @Override + public VersionId versionNumber() { + return TransformConfigVersion.CURRENT; + } +} diff --git a/x-pack/plugin/core/src/main/resources/META-INF/services/org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber b/x-pack/plugin/core/src/main/resources/META-INF/services/org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber new file mode 100644 index 0000000000000..078217faee53a --- /dev/null +++ b/x-pack/plugin/core/src/main/resources/META-INF/services/org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber @@ -0,0 +1,2 @@ +org.elasticsearch.xpack.core.ml.MlConfigVersionComponent +org.elasticsearch.xpack.core.transform.TransformConfigVersionComponent diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentActionTests.java index 6144ae74c7692..ca482decd63a4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentActionTests.java @@ -105,6 +105,7 @@ public void testDoExecute() throws Exception { Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, n, null, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGeneratorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGeneratorTests.java index 02b088e245120..04bb3391f16a8 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGeneratorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGeneratorTests.java @@ -54,6 +54,7 @@ import java.time.Duration; import java.time.Instant; import java.util.List; +import java.util.Map; import java.util.Set; import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; @@ -233,6 +234,7 @@ public Answer answerNullHttpInfo(InvocationOnMock invocationO Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, DiscoveryNodeUtils.builder("1").name("node-name").roles(Set.of()).build(), null, @@ -267,6 +269,7 @@ private Answer answerWithInfo(InvocationOnMock invocationOnMo Version.CURRENT, TransportVersion.current(), IndexVersion.current(), + Map.of(), null, DiscoveryNodeUtils.builder("1").name("node-name").roles(Set.of()).build(), null, From a501a4292f4a88a08b4cb04417496083d5419eae Mon Sep 17 00:00:00 2001 From: Dianna Hohensee Date: Thu, 21 Sep 2023 15:18:22 -0400 Subject: [PATCH 023/155] Pass Executor through TransportAction constructors (#99392) Rather than passing Strings around that are eventually converted to Executors, simply pass the Executors. Add some temporary constructors for serverless compatibility. Related to #97879 --- .../DataStreamsStatsTransportAction.java | 2 +- .../GeoIpDownloaderStatsTransportAction.java | 2 +- .../action/PainlessExecuteAction.java | 2 +- .../reindex/TransportRethrottleAction.java | 2 +- .../reindex/UpdateByQueryWithScriptTests.java | 2 -- .../node/tasks/CancellableTasksIT.java | 8 ++++- ...tReplicationActionRetryOnClosedNodeIT.java | 2 +- .../ClusterFormationInfoAction.java | 2 +- .../CoordinationDiagnosticsAction.java | 2 +- .../TransportNodesHotThreadsAction.java | 2 +- .../node/info/TransportNodesInfoAction.java | 2 +- ...nsportNodesReloadSecureSettingsAction.java | 2 +- .../TransportPrevalidateShardPathAction.java | 2 +- .../node/stats/TransportNodesStatsAction.java | 2 +- .../cancel/TransportCancelTasksAction.java | 2 +- .../tasks/list/TransportListTasksAction.java | 2 +- .../node/usage/TransportNodesUsageAction.java | 2 +- .../status/TransportNodesSnapshotsStatus.java | 2 +- .../stats/TransportClusterStatsAction.java | 2 +- .../analyze/TransportAnalyzeAction.java | 2 +- .../TransportReloadAnalyzersAction.java | 2 +- .../TransportClearIndicesCacheAction.java | 2 +- ...TransportVerifyShardBeforeCloseAction.java | 2 +- .../TransportFindDanglingIndexAction.java | 2 +- .../TransportListDanglingIndicesAction.java | 2 +- .../TransportAnalyzeIndexDiskUsageAction.java | 2 +- .../flush/TransportShardFlushAction.java | 2 +- .../forcemerge/TransportForceMergeAction.java | 2 +- .../TransportGetFieldMappingsIndexAction.java | 2 +- .../TransportVerifyShardIndexBlockAction.java | 2 +- .../recovery/TransportRecoveryAction.java | 2 +- .../refresh/TransportShardRefreshAction.java | 2 +- ...ansportUnpromotableShardRefreshAction.java | 2 +- .../TransportIndicesSegmentsAction.java | 2 +- .../stats/TransportFieldUsageAction.java | 2 +- .../stats/TransportIndicesStatsAction.java | 2 +- .../query/TransportValidateQueryAction.java | 2 +- .../action/bulk/TransportBulkAction.java | 3 +- .../explain/TransportExplainAction.java | 7 ++-- .../TransportFieldCapabilitiesAction.java | 8 ++++- .../action/get/TransportGetAction.java | 13 ++++---- .../get/TransportGetFromTranslogAction.java | 2 +- .../get/TransportShardMultiGetAction.java | 20 ++++++------ ...ansportShardMultiGetFomTranslogAction.java | 2 +- .../search/TransportSearchShardsAction.java | 8 ++++- .../support/HandledTransportAction.java | 32 ++++++++++++------- .../broadcast/TransportBroadcastAction.java | 7 ++-- .../node/TransportBroadcastByNodeAction.java | 9 +++--- .../TransportBroadcastUnpromotableAction.java | 25 ++++++++++++++- .../support/nodes/TransportNodesAction.java | 26 ++++++++++++--- .../TransportReplicationAction.java | 10 +++--- .../replication/TransportWriteAction.java | 3 +- .../shard/TransportSingleShardAction.java | 9 +++--- .../support/tasks/TransportTasksAction.java | 14 +++----- .../TransportShardMultiTermsVectorAction.java | 8 +++-- .../TransportTermVectorsAction.java | 7 ++-- ...ransportNodesListGatewayStartedShards.java | 2 +- .../stats/HealthApiStatsTransportAction.java | 2 +- .../seqno/GlobalCheckpointSyncAction.java | 2 +- .../index/seqno/RetentionLeaseActions.java | 2 +- .../RetentionLeaseBackgroundSyncAction.java | 2 +- .../TransportNodesListShardStoreMetadata.java | 2 +- ...nsportDeleteDesiredBalanceActionTests.java | 1 - ...TransportGetDesiredBalanceActionTests.java | 9 ++---- .../ClusterFormationInfoActionTests.java | 9 +++--- .../MasterHistoryActionTests.java | 11 ++----- .../node/tasks/TaskManagerTestCase.java | 2 +- .../cluster/node/tasks/TestTaskPlugin.java | 4 +-- .../node/tasks/TransportTasksActionTests.java | 2 +- .../bulk/TransportBulkActionIngestTests.java | 11 ++----- .../TransportBroadcastByNodeActionTests.java | 2 +- ...sportBroadcastUnpromotableActionTests.java | 3 +- .../nodes/TransportNodesActionTests.java | 9 +++--- .../TransportReplicationActionTests.java | 3 +- ...ReplicationAllPermitsAcquisitionTests.java | 3 +- .../persistent/TestPersistentTasksPlugin.java | 2 +- .../seektracker/TransportSeekStatsAction.java | 2 +- .../action/TransportAnalyticsStatsAction.java | 2 +- .../TransportAnalyticsStatsActionTests.java | 26 ++++++++++----- .../xpack/ccr/action/ShardChangesAction.java | 2 +- .../action/TransportFollowStatsAction.java | 2 +- .../action/TransportForgetFollowerAction.java | 2 +- .../ClearCcrRestoreSessionAction.java | 8 ++++- .../GetCcrRestoreFileChunkAction.java | 8 ++++- .../PutCcrRestoreSessionAction.java | 2 +- .../TransportNodeDeprecationCheckAction.java | 2 +- .../TransportDeprecationCacheResetAction.java | 2 +- ...nsportNodeDeprecationCheckActionTests.java | 24 ++++++++++---- .../TransportDownsampleIndexerAction.java | 2 +- .../action/EnrichCoordinatorStatsAction.java | 2 +- .../action/EnrichShardMultiSearchAction.java | 2 +- .../eql/plugin/TransportEqlStatsAction.java | 2 +- .../operator/exchange/ExchangeService.java | 19 ++++------- .../esql/plugin/TransportEsqlQueryAction.java | 3 +- .../esql/plugin/TransportEsqlStatsAction.java | 2 +- .../esql/EsqlInfoTransportActionTests.java | 3 -- .../GetGlobalCheckpointsShardAction.java | 2 +- .../TransportClearDeploymentCacheAction.java | 4 +-- .../ml/action/TransportCloseJobAction.java | 3 +- .../TransportDeleteExpiredDataAction.java | 13 +++----- .../ml/action/TransportFlushJobAction.java | 4 +-- .../ml/action/TransportForecastJobAction.java | 4 +-- ...sportGetDataFrameAnalyticsStatsAction.java | 2 +- ...ransportGetDatafeedRunningStateAction.java | 2 +- .../TransportGetDeploymentStatsAction.java | 2 +- .../action/TransportGetJobsStatsAction.java | 2 +- ...portInferTrainedModelDeploymentAction.java | 4 +-- .../TransportIsolateDatafeedAction.java | 2 +- .../ml/action/TransportJobTaskAction.java | 3 +- .../ml/action/TransportKillProcessAction.java | 2 +- .../ml/action/TransportPersistJobAction.java | 4 +-- .../ml/action/TransportPostDataAction.java | 4 +-- ...TransportStopDataFrameAnalyticsAction.java | 3 +- .../action/TransportStopDatafeedAction.java | 2 +- ...sportStopTrainedModelDeploymentAction.java | 4 +-- .../TransportTrainedModelCacheInfoAction.java | 2 +- .../action/TransportUpdateProcessAction.java | 4 +-- ...TransportDeleteExpiredDataActionTests.java | 11 ++----- .../TransportMonitoringBulkActionTests.java | 9 ++---- ...rtClearRepositoriesStatsArchiveAction.java | 2 +- .../TransportRepositoriesStatsAction.java | 2 +- .../TransportDeleteRollupJobAction.java | 4 +-- .../action/TransportGetRollupCapsAction.java | 9 +++++- .../TransportGetRollupIndexCapsAction.java | 9 +++++- .../action/TransportGetRollupJobAction.java | 4 +-- .../action/TransportStartRollupAction.java | 4 +-- .../action/TransportStopRollupAction.java | 3 +- ...actTransportSearchableSnapshotsAction.java | 5 +-- ...rtClearSearchableSnapshotsCacheAction.java | 2 +- ...ansportSearchableSnapshotsStatsAction.java | 2 +- ...rtSearchableSnapshotCacheStoresAction.java | 2 +- ...rchableSnapshotsNodeCachesStatsAction.java | 2 +- .../TransportClearSecurityCacheAction.java | 2 +- .../TransportClearPrivilegesCacheAction.java | 2 +- .../realm/TransportClearRealmCacheAction.java | 2 +- .../role/TransportClearRolesCacheAction.java | 2 +- ...tServiceAccountNodesCredentialsAction.java | 2 +- .../TransportGrantApiKeyActionTests.java | 2 -- ...nsportProfileHasPrivilegesActionTests.java | 3 -- .../TransportClearRealmCacheActionTests.java | 18 ++++++++--- .../TransportCreateTokenActionTests.java | 4 --- .../TransportInvalidateTokenActionTests.java | 4 --- .../blobstore/testkit/BlobAnalyzeAction.java | 8 ++++- .../testkit/GetBlobChecksumAction.java | 8 ++++- .../testkit/RegisterAnalyzeAction.java | 8 ++++- .../testkit/RepositoryAnalyzeAction.java | 3 +- .../action/SpatialStatsTransportAction.java | 2 +- .../SpatialStatsTransportActionTests.java | 17 +++++++--- .../sql/plugin/TransportSqlStatsAction.java | 2 +- .../TransportGetTransformStatsAction.java | 4 +-- .../TransportScheduleNowTransformAction.java | 3 +- .../action/TransportStopTransformAction.java | 3 +- .../TransportUpdateTransformAction.java | 3 +- .../actions/TransportWatcherStatsAction.java | 2 +- .../TransportWatcherStatsActionTests.java | 16 ++++++++-- 155 files changed, 437 insertions(+), 318 deletions(-) diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java index e63825509d551..029fd88abd9c6 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java @@ -70,7 +70,7 @@ public DataStreamsStatsTransportAction( actionFilters, indexNameExpressionResolver, DataStreamsStatsAction.Request::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); this.clusterService = clusterService; this.indicesService = indicesService; diff --git a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/stats/GeoIpDownloaderStatsTransportAction.java b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/stats/GeoIpDownloaderStatsTransportAction.java index dcd8acd5045b9..57dea7b243d48 100644 --- a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/stats/GeoIpDownloaderStatsTransportAction.java +++ b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/stats/GeoIpDownloaderStatsTransportAction.java @@ -52,7 +52,7 @@ public GeoIpDownloaderStatsTransportAction( actionFilters, Request::new, NodeRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.transportService = transportService; this.registry = registry; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java index b098054210592..783abf5551c43 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java @@ -515,7 +515,7 @@ public TransportAction( // Creating a in-memory index is not light weight // TODO: is MANAGEMENT TP the right TP? Right now this is an admin api (see action name). Request::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.scriptService = scriptService; this.indicesServices = indicesServices; diff --git a/modules/reindex/src/main/java/org/elasticsearch/reindex/TransportRethrottleAction.java b/modules/reindex/src/main/java/org/elasticsearch/reindex/TransportRethrottleAction.java index df97afa0a5053..d9c759d784075 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/reindex/TransportRethrottleAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/reindex/TransportRethrottleAction.java @@ -46,7 +46,7 @@ public TransportRethrottleAction( RethrottleRequest::new, ListTasksResponse::new, TaskInfo::from, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); this.client = client; } diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryWithScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryWithScriptTests.java index 059718754e815..876ddefda161b 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryWithScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryWithScriptTests.java @@ -52,8 +52,6 @@ protected UpdateByQueryRequest request() { @Override protected TransportUpdateByQueryAction.AsyncIndexBySearchAction action(ScriptService scriptService, UpdateByQueryRequest request) { TransportService transportService = mock(TransportService.class); - - // TODO: temporary, remove in #97879 when(transportService.getThreadPool()).thenReturn(threadPool); TransportUpdateByQueryAction transportAction = new TransportUpdateByQueryAction( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/CancellableTasksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/CancellableTasksIT.java index 2645243916d76..bbd1ea67b7ef8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/CancellableTasksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/CancellableTasksIT.java @@ -512,7 +512,13 @@ public static class TransportTestAction extends HandledTransportAction getResponseReader() { } @Override - protected String getExecutor(GetRequest request, ShardId shardId) { + protected Executor getExecutor(GetRequest request, ShardId shardId) { final ClusterState clusterState = clusterService.state(); if (clusterState.metadata().getIndexSafe(shardId.getIndex()).isSystem()) { - return executorSelector.executorForGet(shardId.getIndexName()); + return threadPool.executor(executorSelector.executorForGet(shardId.getIndexName())); } else if (indicesService.indexServiceSafe(shardId.getIndex()).getIndexSettings().isSearchThrottled()) { - return ThreadPool.Names.SEARCH_THROTTLED; + return threadPool.executor(ThreadPool.Names.SEARCH_THROTTLED); } else { return super.getExecutor(request, shardId); } @@ -168,7 +169,7 @@ protected String getExecutor(GetRequest request, ShardId shardId) { private void asyncGet(GetRequest request, ShardId shardId, ActionListener listener) throws IOException { if (request.refresh() && request.realtime() == false) { - threadPool.executor(getExecutor(request, shardId)).execute(ActionRunnable.wrap(listener, l -> { + getExecutor(request, shardId).execute(ActionRunnable.wrap(listener, l -> { var indexShard = getIndexShard(shardId); indexShard.externalRefresh("refresh_flag_get", l.map(r -> shardOperation(request, shardId))); })); @@ -218,7 +219,7 @@ private void handleGetOnUnpromotableShard(GetRequest request, IndexShard indexSh ); } } - }), TransportGetFromTranslogAction.Response::new, threadPool.executor(getExecutor(request, shardId))) + }), TransportGetFromTranslogAction.Response::new, getExecutor(request, shardId)) ); } else { // A non-real-time get with no explicit refresh requested. diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java index 37e07b3ca2e07..553da3d845b54 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java @@ -50,7 +50,7 @@ public class TransportGetFromTranslogAction extends HandledTransportAction< @Inject public TransportGetFromTranslogAction(TransportService transportService, IndicesService indicesService, ActionFilters actionFilters) { - super(NAME, transportService, actionFilters, Request::new, ThreadPool.Names.GET); + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.GET)); this.indicesService = indicesService; } diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java index 7b7fcbc41d956..83d5e6a89d138 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java @@ -39,6 +39,7 @@ import org.elasticsearch.transport.TransportService; import java.io.IOException; +import java.util.concurrent.Executor; import static org.elasticsearch.action.get.TransportGetAction.getCurrentNodeOfPrimary; import static org.elasticsearch.core.Strings.format; @@ -72,7 +73,7 @@ public TransportShardMultiGetAction( actionFilters, indexNameExpressionResolver, MultiGetShardRequest::new, - ThreadPool.Names.GET + threadPool.executor(ThreadPool.Names.GET) ); this.indicesService = indicesService; this.executorSelector = executorSelector; @@ -144,12 +145,12 @@ protected MultiGetShardResponse shardOperation(MultiGetShardRequest request, Sha } @Override - protected String getExecutor(MultiGetShardRequest request, ShardId shardId) { + protected Executor getExecutor(MultiGetShardRequest request, ShardId shardId) { final ClusterState clusterState = clusterService.state(); if (clusterState.metadata().index(shardId.getIndex()).isSystem()) { - return executorSelector.executorForGet(shardId.getIndexName()); + return threadPool.executor(executorSelector.executorForGet(shardId.getIndexName())); } else if (indicesService.indexServiceSafe(shardId.getIndex()).getIndexSettings().isSearchThrottled()) { - return ThreadPool.Names.SEARCH_THROTTLED; + return threadPool.executor(ThreadPool.Names.SEARCH_THROTTLED); } else { return super.getExecutor(request, shardId); } @@ -205,15 +206,14 @@ private void handleMultiGetOnUnpromotableShard( indexShard.waitForSegmentGeneration( r.segmentGeneration(), listener.delegateFailureAndWrap( - (ll, aLong) -> threadPool.executor(getExecutor(request, shardId)) - .execute( - ActionRunnable.supply(ll, () -> handleLocalGets(request, r.multiGetShardResponse(), shardId)) - ) + (ll, aLong) -> getExecutor(request, shardId).execute( + ActionRunnable.supply(ll, () -> handleLocalGets(request, r.multiGetShardResponse(), shardId)) + ) ) ); } } - }), TransportShardMultiGetFomTranslogAction.Response::new, threadPool.executor(getExecutor(request, shardId))) + }), TransportShardMultiGetFomTranslogAction.Response::new, getExecutor(request, shardId)) ); } else { // A non-real-time mget with no explicit refresh requested. @@ -262,7 +262,7 @@ private void getAndAddToResponse(ShardId shardId, int location, MultiGetShardReq private void asyncShardMultiGet(MultiGetShardRequest request, ShardId shardId, ActionListener listener) throws IOException { if (request.refresh() && request.realtime() == false) { - threadPool.executor(getExecutor(request, shardId)).execute(ActionRunnable.wrap(listener, l -> { + getExecutor(request, shardId).execute(ActionRunnable.wrap(listener, l -> { var indexShard = getIndexShard(shardId); indexShard.externalRefresh("refresh_flag_mget", l.map(r -> shardOperation(request, shardId))); })); diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java index cea18fe09b6bc..032f002a675c4 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java @@ -48,7 +48,7 @@ protected TransportShardMultiGetFomTranslogAction( IndicesService indicesService, ActionFilters actionFilters ) { - super(NAME, transportService, actionFilters, Request::new, ThreadPool.Names.GET); + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.GET)); this.indicesService = indicesService; } diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportSearchShardsAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportSearchShardsAction.java index 19af8ad8a1beb..4c8ade4d78ead 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportSearchShardsAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportSearchShardsAction.java @@ -60,7 +60,13 @@ public TransportSearchShardsAction( SearchTransportService searchTransportService, IndexNameExpressionResolver indexNameExpressionResolver ) { - super(SearchShardsAction.NAME, transportService, actionFilters, SearchShardsRequest::new, ThreadPool.Names.SEARCH_COORDINATION); + super( + SearchShardsAction.NAME, + transportService, + actionFilters, + SearchShardsRequest::new, + transportService.getThreadPool().executor(ThreadPool.Names.SEARCH_COORDINATION) + ); this.transportService = transportService; this.transportSearchAction = transportSearchAction; this.searchService = searchService; diff --git a/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java b/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java index a0e36a538913d..9255ea5daddb2 100644 --- a/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java @@ -10,12 +10,14 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportChannel; import org.elasticsearch.transport.TransportRequestHandler; import org.elasticsearch.transport.TransportService; +import java.util.concurrent.Executor; + /** * A TransportAction that self registers a handler into the transport service */ @@ -37,11 +39,24 @@ protected HandledTransportAction( TransportService transportService, ActionFilters actionFilters, Writeable.Reader requestReader, - String executor + Executor executor ) { this(actionName, true, transportService, actionFilters, requestReader, executor); } + /** + * Temporary for serverless compatibility. TODO remove. + */ + protected HandledTransportAction( + String actionName, + TransportService transportService, + ActionFilters actionFilters, + Writeable.Reader requestReader, + String executor + ) { + this(actionName, true, transportService, actionFilters, requestReader, transportService.getThreadPool().executor(executor)); + } + protected HandledTransportAction( String actionName, boolean canTripCircuitBreaker, @@ -49,7 +64,7 @@ protected HandledTransportAction( ActionFilters actionFilters, Writeable.Reader requestReader ) { - this(actionName, canTripCircuitBreaker, transportService, actionFilters, requestReader, ThreadPool.Names.SAME); + this(actionName, canTripCircuitBreaker, transportService, actionFilters, requestReader, EsExecutors.DIRECT_EXECUTOR_SERVICE); } protected HandledTransportAction( @@ -58,17 +73,10 @@ protected HandledTransportAction( TransportService transportService, ActionFilters actionFilters, Writeable.Reader requestReader, - String executor + Executor executor ) { super(actionName, actionFilters, transportService.getTaskManager()); - transportService.registerRequestHandler( - actionName, - transportService.getThreadPool().executor(executor), - false, - canTripCircuitBreaker, - requestReader, - new TransportHandler() - ); + transportService.registerRequestHandler(actionName, executor, false, canTripCircuitBreaker, requestReader, new TransportHandler()); } class TransportHandler implements TransportRequestHandler { diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java index ddd2cc43005e2..6be22f28e8ad9 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java @@ -30,7 +30,6 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.Transports; @@ -62,15 +61,15 @@ protected TransportBroadcastAction( IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader requestReader, Writeable.Reader shardRequestReader, - String executor + Executor executor ) { // TODO replace SAME when removing workaround for https://github.com/elastic/elasticsearch/issues/97916 - super(actionName, transportService, actionFilters, requestReader, ThreadPool.Names.SAME); + super(actionName, transportService, actionFilters, requestReader, EsExecutors.DIRECT_EXECUTOR_SERVICE); this.clusterService = clusterService; this.transportService = transportService; this.indexNameExpressionResolver = indexNameExpressionResolver; this.transportShardAction = actionName + "[s]"; - this.executor = transportService.getThreadPool().executor(executor); + this.executor = executor; assert this.executor != EsExecutors.DIRECT_EXECUTOR_SERVICE : "O(#shards) work must always fork to an appropriate executor"; transportService.registerRequestHandler( diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java index 4f78d68daa8f2..4eeb3af4810cc 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java @@ -40,7 +40,6 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportChannel; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestHandler; @@ -91,7 +90,7 @@ public TransportBroadcastByNodeAction( ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader request, - String executor + Executor executor ) { this(actionName, clusterService, transportService, actionFilters, indexNameExpressionResolver, request, executor, true); } @@ -103,16 +102,16 @@ public TransportBroadcastByNodeAction( ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader request, - String executor, + Executor executor, boolean canTripCircuitBreaker ) { // TODO replace SAME when removing workaround for https://github.com/elastic/elasticsearch/issues/97916 - super(actionName, canTripCircuitBreaker, transportService, actionFilters, request, ThreadPool.Names.SAME); + super(actionName, canTripCircuitBreaker, transportService, actionFilters, request, EsExecutors.DIRECT_EXECUTOR_SERVICE); this.clusterService = clusterService; this.transportService = transportService; this.indexNameExpressionResolver = indexNameExpressionResolver; - this.executor = transportService.getThreadPool().executor(executor); + this.executor = executor; assert this.executor != EsExecutors.DIRECT_EXECUTOR_SERVICE : "O(#shards) work must always fork to an appropriate executor"; transportNodeBroadcastAction = actionName + "[n]"; diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java index 579ed101b56ae..75ba30e63763b 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java @@ -44,6 +44,9 @@ public abstract class TransportBroadcastUnpromotableAction requestReader, String executor + ) { + this( + actionName, + clusterService, + transportService, + shardStateAction, + actionFilters, + requestReader, + transportService.getThreadPool().executor(executor) + ); + } + + protected TransportBroadcastUnpromotableAction( + String actionName, + ClusterService clusterService, + TransportService transportService, + ShardStateAction shardStateAction, + ActionFilters actionFilters, + Writeable.Reader requestReader, + Executor executor ) { super(actionName, transportService, actionFilters, requestReader); this.clusterService = clusterService; this.shardStateAction = shardStateAction; this.transportService = transportService; this.transportUnpromotableAction = actionName + "[u]"; - this.executor = transportService.getThreadPool().executor(executor); + this.executor = executor; transportService.registerRequestHandler( transportUnpromotableAction, diff --git a/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java b/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java index b45c448ecc52c..3a4b7fb0fa3bb 100644 --- a/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -55,6 +56,22 @@ public abstract class TransportNodesAction< private final Executor finalExecutor; + /** + * Temporary for serverless compatibility. TODO remove. + */ + protected TransportNodesAction( + String actionName, + ThreadPool threadPool, + ClusterService clusterService, + TransportService transportService, + ActionFilters actionFilters, + Writeable.Reader request, + Writeable.Reader nodeRequest, + String executor + ) { + this(actionName, threadPool, clusterService, transportService, actionFilters, request, nodeRequest, threadPool.executor(executor)); + } + /** * @param actionName action name * @param threadPool thread-pool @@ -73,14 +90,15 @@ protected TransportNodesAction( ActionFilters actionFilters, Writeable.Reader request, Writeable.Reader nodeRequest, - String executor + Executor executor ) { // coordination can run on SAME because it's only O(#nodes) work - super(actionName, transportService, actionFilters, request, ThreadPool.Names.SAME); - assert executor.equals(ThreadPool.Names.SAME) == false : "TransportNodesAction must always fork off the transport thread"; + super(actionName, transportService, actionFilters, request, EsExecutors.DIRECT_EXECUTOR_SERVICE); + assert executor.equals(EsExecutors.DIRECT_EXECUTOR_SERVICE) == false + : "TransportNodesAction must always fork off the transport thread"; this.clusterService = Objects.requireNonNull(clusterService); this.transportService = Objects.requireNonNull(transportService); - this.finalExecutor = threadPool.executor(executor); + this.finalExecutor = executor; this.transportNodeAction = actionName + "[n]"; transportService.registerRequestHandler(transportNodeAction, finalExecutor, nodeRequest, new NodeTransportHandler()); } diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index b3f0496597009..9adf4881a1ef6 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -139,7 +139,7 @@ protected TransportReplicationAction( ActionFilters actionFilters, Writeable.Reader requestReader, Writeable.Reader replicaRequestReader, - String executor + Executor executor ) { this( settings, @@ -169,7 +169,7 @@ protected TransportReplicationAction( ActionFilters actionFilters, Writeable.Reader requestReader, Writeable.Reader replicaRequestReader, - String executor, + Executor executor, boolean syncGlobalCheckpointAfterOperation, boolean forceExecutionOnPrimary ) { @@ -179,7 +179,7 @@ protected TransportReplicationAction( this.clusterService = clusterService; this.indicesService = indicesService; this.shardStateAction = shardStateAction; - this.executor = threadPool.executor(executor); + this.executor = executor; this.transportPrimaryAction = actionName + "[p]"; this.transportReplicaAction = actionName + "[r]"; @@ -197,7 +197,7 @@ protected TransportReplicationAction( transportService.registerRequestHandler( transportPrimaryAction, - threadPool.executor(executor), + executor, forceExecutionOnPrimary, true, in -> new ConcreteShardRequest<>(requestReader, in), @@ -207,7 +207,7 @@ protected TransportReplicationAction( // we must never reject on because of thread pool capacity on replicas transportService.registerRequestHandler( transportReplicaAction, - threadPool.executor(executor), + executor, true, true, in -> new ConcreteReplicaRequest<>(replicaRequestReader, in), diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java index 6101ea234947a..ea24d7deb9aa7 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.TimeValue; @@ -91,7 +92,7 @@ protected TransportWriteAction( actionFilters, request, replicaRequest, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, true, forceExecutionOnPrimary ); diff --git a/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java b/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java index 9d980fabc6714..f4a12cae6258d 100644 --- a/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java @@ -40,6 +40,7 @@ import org.elasticsearch.transport.TransportService; import java.io.IOException; +import java.util.concurrent.Executor; import static org.elasticsearch.action.support.TransportActions.isShardNotAvailableException; import static org.elasticsearch.core.Strings.format; @@ -58,7 +59,7 @@ public abstract class TransportSingleShardAction request, - String executor + Executor executor ) { super(actionName, actionFilters, transportService.getTaskManager()); this.threadPool = threadPool; @@ -107,7 +108,7 @@ protected void doExecute(Task task, Request request, ActionListener li protected abstract Response shardOperation(Request request, ShardId shardId) throws IOException; protected void asyncShardOperation(Request request, ShardId shardId, ActionListener listener) throws IOException { - threadPool.executor(getExecutor(request, shardId)).execute(ActionRunnable.supply(listener, () -> shardOperation(request, shardId))); + getExecutor(request, shardId).execute(ActionRunnable.supply(listener, () -> shardOperation(request, shardId))); } protected abstract Writeable.Reader getResponseReader(); @@ -284,7 +285,7 @@ public String concreteIndex() { } } - protected String getExecutor(Request request, ShardId shardId) { + protected Executor getExecutor(Request request, ShardId shardId) { return executor; } } diff --git a/server/src/main/java/org/elasticsearch/action/support/tasks/TransportTasksAction.java b/server/src/main/java/org/elasticsearch/action/support/tasks/TransportTasksAction.java index fb29e8a838db6..af87fd8cddb4b 100644 --- a/server/src/main/java/org/elasticsearch/action/support/tasks/TransportTasksAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/tasks/TransportTasksAction.java @@ -25,10 +25,10 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportChannel; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestHandler; @@ -42,6 +42,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; /** * The base class for transport actions that are interacting with currently running tasks. @@ -68,10 +69,10 @@ protected TransportTasksAction( Writeable.Reader requestReader, Writeable.Reader responsesReader, Writeable.Reader responseReader, - String nodeExecutor + Executor nodeExecutor ) { // coordination can run on SAME because it's only O(#nodes) work - super(actionName, transportService, actionFilters, requestReader, ThreadPool.Names.SAME); + super(actionName, transportService, actionFilters, requestReader, EsExecutors.DIRECT_EXECUTOR_SERVICE); this.clusterService = clusterService; this.transportService = transportService; this.transportNodeAction = actionName + "[n]"; @@ -79,12 +80,7 @@ protected TransportTasksAction( this.responsesReader = responsesReader; this.responseReader = responseReader; - transportService.registerRequestHandler( - transportNodeAction, - transportService.getThreadPool().executor(nodeExecutor), - NodeTaskRequest::new, - new NodeTransportHandler() - ); + transportService.registerRequestHandler(transportNodeAction, nodeExecutor, NodeTaskRequest::new, new NodeTransportHandler()); } @Override diff --git a/server/src/main/java/org/elasticsearch/action/termvectors/TransportShardMultiTermsVectorAction.java b/server/src/main/java/org/elasticsearch/action/termvectors/TransportShardMultiTermsVectorAction.java index 3499496097580..e7de3d7e70cfc 100644 --- a/server/src/main/java/org/elasticsearch/action/termvectors/TransportShardMultiTermsVectorAction.java +++ b/server/src/main/java/org/elasticsearch/action/termvectors/TransportShardMultiTermsVectorAction.java @@ -26,6 +26,8 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.concurrent.Executor; + import static org.elasticsearch.core.Strings.format; public class TransportShardMultiTermsVectorAction extends TransportSingleShardAction< @@ -54,7 +56,7 @@ public TransportShardMultiTermsVectorAction( actionFilters, indexNameExpressionResolver, MultiTermVectorsShardRequest::new, - ThreadPool.Names.GET + threadPool.executor(ThreadPool.Names.GET) ); this.indicesService = indicesService; } @@ -108,10 +110,10 @@ protected MultiTermVectorsShardResponse shardOperation(MultiTermVectorsShardRequ } @Override - protected String getExecutor(MultiTermVectorsShardRequest request, ShardId shardId) { + protected Executor getExecutor(MultiTermVectorsShardRequest request, ShardId shardId) { IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); return indexService.getIndexSettings().isSearchThrottled() - ? ThreadPool.Names.SEARCH_THROTTLED + ? threadPool.executor(ThreadPool.Names.SEARCH_THROTTLED) : super.getExecutor(request, shardId); } } diff --git a/server/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java b/server/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java index 845a848637c45..1375ae7fb6c3a 100644 --- a/server/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java +++ b/server/src/main/java/org/elasticsearch/action/termvectors/TransportTermVectorsAction.java @@ -27,6 +27,7 @@ import org.elasticsearch.transport.TransportService; import java.io.IOException; +import java.util.concurrent.Executor; /** * Performs the get operation. @@ -52,7 +53,7 @@ public TransportTermVectorsAction( actionFilters, indexNameExpressionResolver, TermVectorsRequest::new, - ThreadPool.Names.GET + threadPool.executor(ThreadPool.Names.GET) ); this.indicesService = indicesService; @@ -114,10 +115,10 @@ protected Writeable.Reader getResponseReader() { } @Override - protected String getExecutor(TermVectorsRequest request, ShardId shardId) { + protected Executor getExecutor(TermVectorsRequest request, ShardId shardId) { IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); return indexService.getIndexSettings().isSearchThrottled() - ? ThreadPool.Names.SEARCH_THROTTLED + ? threadPool.executor(ThreadPool.Names.SEARCH_THROTTLED) : super.getExecutor(request, shardId); } } diff --git a/server/src/main/java/org/elasticsearch/gateway/TransportNodesListGatewayStartedShards.java b/server/src/main/java/org/elasticsearch/gateway/TransportNodesListGatewayStartedShards.java index 0244e3bf77d60..a0246eb29de82 100644 --- a/server/src/main/java/org/elasticsearch/gateway/TransportNodesListGatewayStartedShards.java +++ b/server/src/main/java/org/elasticsearch/gateway/TransportNodesListGatewayStartedShards.java @@ -87,7 +87,7 @@ public TransportNodesListGatewayStartedShards( actionFilters, Request::new, NodeRequest::new, - ThreadPool.Names.FETCH_SHARD_STARTED + threadPool.executor(ThreadPool.Names.FETCH_SHARD_STARTED) ); this.settings = settings; this.nodeEnv = env; diff --git a/server/src/main/java/org/elasticsearch/health/stats/HealthApiStatsTransportAction.java b/server/src/main/java/org/elasticsearch/health/stats/HealthApiStatsTransportAction.java index 890403a48e2e7..55d496e2b8c59 100644 --- a/server/src/main/java/org/elasticsearch/health/stats/HealthApiStatsTransportAction.java +++ b/server/src/main/java/org/elasticsearch/health/stats/HealthApiStatsTransportAction.java @@ -48,7 +48,7 @@ public HealthApiStatsTransportAction( actionFilters, HealthApiStatsAction.Request::new, HealthApiStatsAction.Request.Node::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.healthApiStats = healthApiStats; } diff --git a/server/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java b/server/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java index 782a49ab7228a..1f8a863fe3e7b 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncAction.java @@ -62,7 +62,7 @@ public GlobalCheckpointSyncAction( actionFilters, Request::new, Request::new, - ThreadPool.Names.WRITE, + threadPool.executor(ThreadPool.Names.WRITE), false, true ); diff --git a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java index 2866d1b0ee13a..9cc9e79ebb11a 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseActions.java @@ -69,7 +69,7 @@ abstract static class TransportRetentionLeaseAction> extend actionFilters, indexNameExpressionResolver, requestSupplier, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.indicesService = Objects.requireNonNull(indicesService); } diff --git a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseBackgroundSyncAction.java b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseBackgroundSyncAction.java index 06d0494b1bdec..5046ea1cb4d0d 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseBackgroundSyncAction.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/RetentionLeaseBackgroundSyncAction.java @@ -81,7 +81,7 @@ public RetentionLeaseBackgroundSyncAction( actionFilters, Request::new, Request::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/server/src/main/java/org/elasticsearch/indices/store/TransportNodesListShardStoreMetadata.java b/server/src/main/java/org/elasticsearch/indices/store/TransportNodesListShardStoreMetadata.java index 479b42fdf2e87..bbadd7038b6e5 100644 --- a/server/src/main/java/org/elasticsearch/indices/store/TransportNodesListShardStoreMetadata.java +++ b/server/src/main/java/org/elasticsearch/indices/store/TransportNodesListShardStoreMetadata.java @@ -87,7 +87,7 @@ public TransportNodesListShardStoreMetadata( actionFilters, Request::new, NodeRequest::new, - ThreadPool.Names.FETCH_SHARD_STORE + threadPool.executor(ThreadPool.Names.FETCH_SHARD_STORE) ); this.settings = settings; this.indicesService = indicesService; diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceActionTests.java index a2bfa68fdf25f..cbe4acd137b2a 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceActionTests.java @@ -135,7 +135,6 @@ public DesiredBalance compute( var listener = new PlainActionFuture(); - // TODO: temporary, remove in #97879 TransportService transportService = mock(TransportService.class); when(transportService.getThreadPool()).thenReturn(threadPool); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java index 80f0b435645e6..1d80454fcea12 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java @@ -36,7 +36,6 @@ import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.shard.ShardId; @@ -44,6 +43,7 @@ import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.AbstractChunkedSerializingTestCase; +import org.elasticsearch.test.MockUtils; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.junit.Before; @@ -61,7 +61,6 @@ import static org.elasticsearch.cluster.ClusterModule.SHARDS_ALLOCATOR_TYPE_SETTING; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -69,16 +68,12 @@ public class TransportGetDesiredBalanceActionTests extends ESAllocationTestCase private final DesiredBalanceShardsAllocator desiredBalanceShardsAllocator = mock(DesiredBalanceShardsAllocator.class); private final ClusterInfoService clusterInfoService = mock(ClusterInfoService.class); - private TransportService transportService = mock(TransportService.class); private ThreadPool threadPool = mock(ThreadPool.class); + private TransportService transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); private TransportGetDesiredBalanceAction transportGetDesiredBalanceAction; @Before public void initialize() { - // TODO: temporary, remove in #97879 - when(transportService.getThreadPool()).thenReturn(threadPool); - when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); - transportGetDesiredBalanceAction = new TransportGetDesiredBalanceAction( transportService, mock(ClusterService.class), diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/ClusterFormationInfoActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/ClusterFormationInfoActionTests.java index f190c422e165c..38c811d367560 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/ClusterFormationInfoActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/ClusterFormationInfoActionTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.monitor.StatusInfo; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; +import org.elasticsearch.test.MockUtils; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -155,17 +156,15 @@ private ClusterFormationFailureHelper.ClusterFormationState getClusterFormationS } public void testTransportDoExecute() { - TransportService transportService = mock(TransportService.class); + ThreadPool threadPool = mock(ThreadPool.class); + when(threadPool.relativeTimeInMillis()).thenReturn(System.currentTimeMillis()); + TransportService transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); ActionFilters actionFilters = mock(ActionFilters.class); ClusterService clusterService = mock(ClusterService.class); when(clusterService.getSettings()).thenReturn(Settings.EMPTY); - ThreadPool threadPool = mock(ThreadPool.class); - when(threadPool.relativeTimeInMillis()).thenReturn(System.currentTimeMillis()); Coordinator coordinator = mock(Coordinator.class); ClusterFormationFailureHelper.ClusterFormationState clusterFormationState = getClusterFormationState(); when(coordinator.getClusterFormationState()).thenReturn(clusterFormationState); - - // TODO: temporary, remove in #97879 when(transportService.getThreadPool()).thenReturn(threadPool); when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); ClusterFormationInfoAction.TransportAction action = new ClusterFormationInfoAction.TransportAction( diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/MasterHistoryActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/MasterHistoryActionTests.java index f362327a3d8e4..327a20e9aedee 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/MasterHistoryActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/coordination/MasterHistoryActionTests.java @@ -16,16 +16,15 @@ import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; +import org.elasticsearch.test.MockUtils; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.util.ArrayList; import java.util.List; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -76,19 +75,15 @@ private MasterHistoryAction.Response mutateMasterHistoryResponse(MasterHistoryAc } public void testTransportDoExecute() { - TransportService transportService = mock(TransportService.class); + ThreadPool threadPool = mock(ThreadPool.class); + TransportService transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); ActionFilters actionFilters = mock(ActionFilters.class); MasterHistoryService masterHistoryService = mock(MasterHistoryService.class); ClusterService clusterService = mock(ClusterService.class); when(clusterService.getSettings()).thenReturn(Settings.EMPTY); - ThreadPool threadPool = mock(ThreadPool.class); when(threadPool.relativeTimeInMillis()).thenReturn(System.currentTimeMillis()); MasterHistory masterHistory = new MasterHistory(threadPool, clusterService); when(masterHistoryService.getLocalMasterHistory()).thenReturn(masterHistory); - - // TODO: temporary, remove in #97879 - when(transportService.getThreadPool()).thenReturn(threadPool); - when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); MasterHistoryAction.TransportAction action = new MasterHistoryAction.TransportAction( transportService, actionFilters, diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java index 78bf709ef42f7..6ca3e6bbe5400 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java @@ -154,7 +154,7 @@ abstract class AbstractTestNodesAction()), request, nodeRequest, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TestTaskPlugin.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TestTaskPlugin.java index e8a3338559947..439c87bdeace6 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TestTaskPlugin.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TestTaskPlugin.java @@ -269,7 +269,7 @@ public TransportTestTaskAction(ThreadPool threadPool, ClusterService clusterServ new ActionFilters(new HashSet<>()), NodesRequest::new, NodeRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); } @@ -404,7 +404,7 @@ public TransportUnblockTestTasksAction(ClusterService clusterService, TransportS UnblockTestTasksRequest::new, UnblockTestTasksResponse::new, UnblockTestTaskResponse::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TransportTasksActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TransportTasksActionTests.java index b0af912519e6d..a650692eea9f4 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TransportTasksActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TransportTasksActionTests.java @@ -265,7 +265,7 @@ protected TestTasksAction(String actionName, ClusterService clusterService, Tran TestTasksRequest::new, TestTasksResponse::new, TestTaskResponse::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java index 85a74174b094f..86146e41f87e1 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.AtomicArray; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; @@ -45,6 +44,7 @@ import org.elasticsearch.ingest.IngestService; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.MockUtils; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool.Names; @@ -67,7 +67,6 @@ import static org.hamcrest.Matchers.sameInstance; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -179,15 +178,9 @@ class TestSingleItemBulkWriteAction extends TransportSingleItemBulkWriteAction request, Writeable.Reader nodeRequest, - String nodeExecutor + Executor nodeExecutor ) { super("indices:admin/test", threadPool, clusterService, transportService, actionFilters, request, nodeRequest, nodeExecutor); } @@ -347,7 +348,7 @@ private static class DataNodesOnlyTransportNodesAction extends TestTransportNode ActionFilters actionFilters, Writeable.Reader request, Writeable.Reader nodeRequest, - String nodeExecutor + Executor nodeExecutor ) { super(threadPool, clusterService, transportService, actionFilters, request, nodeRequest, nodeExecutor); } diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index 308c8a66767fa..1c857d5554113 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -45,6 +45,7 @@ import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Strings; @@ -1504,7 +1505,7 @@ private class TestAction extends TransportReplicationAction()), Request::new, Request::new, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, false, forceExecute ); diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationAllPermitsAcquisitionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationAllPermitsAcquisitionTests.java index 824df41f8cfa7..e687b3b1c377f 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationAllPermitsAcquisitionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationAllPermitsAcquisitionTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexService; @@ -459,7 +460,7 @@ private abstract class TestAction extends TransportReplicationAction()), Request::new, Request::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.shardId = Objects.requireNonNull(shardId); this.primary = Objects.requireNonNull(primary); diff --git a/server/src/test/java/org/elasticsearch/persistent/TestPersistentTasksPlugin.java b/server/src/test/java/org/elasticsearch/persistent/TestPersistentTasksPlugin.java index 580115a1e9af3..d91d595c701eb 100644 --- a/server/src/test/java/org/elasticsearch/persistent/TestPersistentTasksPlugin.java +++ b/server/src/test/java/org/elasticsearch/persistent/TestPersistentTasksPlugin.java @@ -543,7 +543,7 @@ public TransportTestTaskAction(ClusterService clusterService, TransportService t TestTasksRequest::new, TestTasksResponse::new, TestTaskResponse::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/TransportSeekStatsAction.java b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/TransportSeekStatsAction.java index c57d05cf5c530..bfc528f04b215 100644 --- a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/TransportSeekStatsAction.java +++ b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/TransportSeekStatsAction.java @@ -44,7 +44,7 @@ public TransportSeekStatsAction( actionFilters, SeekStatsRequest::new, SeekStatsRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.seekStatsService = seekStatsService; } diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsAction.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsAction.java index 6702e37e8dc4b..fe9b43e814d1c 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsAction.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsAction.java @@ -45,7 +45,7 @@ public TransportAnalyticsStatsAction( actionFilters, AnalyticsStatsAction.Request::new, AnalyticsStatsAction.NodeRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.usage = usage; } diff --git a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsActionTests.java b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsActionTests.java index 6b0b53aedd12d..f5a081217f866 100644 --- a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsActionTests.java +++ b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/action/TransportAnalyticsStatsActionTests.java @@ -14,9 +14,9 @@ import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.ObjectPath; +import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.ContextParser; @@ -26,36 +26,46 @@ import org.elasticsearch.xpack.analytics.AnalyticsUsage; import org.elasticsearch.xpack.core.analytics.AnalyticsFeatureSetUsage; import org.elasticsearch.xpack.core.analytics.action.AnalyticsStatsAction; +import org.junit.After; +import org.junit.Before; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class TransportAnalyticsStatsActionTests extends ESTestCase { + + private static ThreadPool threadPool; + + @Before + public void setup() { + threadPool = new TestThreadPool("TransportAnalyticsStatsActionTests"); + } + + @After + public void cleanup() { + ThreadPool.terminate(threadPool, 30, TimeUnit.SECONDS); + threadPool = null; + } + public TransportAnalyticsStatsAction action(AnalyticsUsage usage) { TransportService transportService = mock(TransportService.class); - ThreadPool threadPool = mock(ThreadPool.class); - - // TODO: temporary, remove in #97879 when(transportService.getThreadPool()).thenReturn(threadPool); - when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); - ClusterService clusterService = mock(ClusterService.class); DiscoveryNode discoveryNode = DiscoveryNodeUtils.create("nodeId"); when(clusterService.localNode()).thenReturn(discoveryNode); ClusterName clusterName = new ClusterName("cluster_name"); when(clusterService.getClusterName()).thenReturn(clusterName); - ClusterState clusterState = mock(ClusterState.class); when(clusterState.getMetadata()).thenReturn(Metadata.EMPTY_METADATA); when(clusterService.state()).thenReturn(clusterState); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardChangesAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardChangesAction.java index d31a6f681cd9e..6b990d033cae5 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardChangesAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardChangesAction.java @@ -363,7 +363,7 @@ public TransportAction( actionFilters, indexNameExpressionResolver, Request::new, - ThreadPool.Names.SEARCH + threadPool.executor(ThreadPool.Names.SEARCH) ); this.indicesService = indicesService; } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowStatsAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowStatsAction.java index bb64dda7c241b..ecca422282736 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowStatsAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowStatsAction.java @@ -61,7 +61,7 @@ public TransportFollowStatsAction( FollowStatsAction.StatsRequest::new, FollowStatsAction.StatsResponses::new, FollowStatsAction.StatsResponse::new, - Ccr.CCR_THREAD_POOL_NAME + transportService.getThreadPool().executor(Ccr.CCR_THREAD_POOL_NAME) ); this.ccrLicenseChecker = Objects.requireNonNull(ccrLicenseChecker); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java index b7d333de80a75..b8b8fa0b32c31 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportForgetFollowerAction.java @@ -65,7 +65,7 @@ public TransportForgetFollowerAction( Objects.requireNonNull(actionFilters), Objects.requireNonNull(indexNameExpressionResolver), ForgetFollowerAction.Request::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); this.clusterService = clusterService; this.indicesService = Objects.requireNonNull(indicesService); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/ClearCcrRestoreSessionAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/ClearCcrRestoreSessionAction.java index e13f6fd5cac80..886eda5659714 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/ClearCcrRestoreSessionAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/ClearCcrRestoreSessionAction.java @@ -47,7 +47,13 @@ private TransportDeleteCcrRestoreSessionAction( TransportService transportService, CcrRestoreSourceService ccrRestoreService ) { - super(actionName, transportService, actionFilters, ClearCcrRestoreSessionRequest::new, ThreadPool.Names.GENERIC); + super( + actionName, + transportService, + actionFilters, + ClearCcrRestoreSessionRequest::new, + transportService.getThreadPool().executor(ThreadPool.Names.GENERIC) + ); TransportActionProxy.registerProxyAction(transportService, actionName, false, in -> ActionResponse.Empty.INSTANCE); this.ccrRestoreService = ccrRestoreService; } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/GetCcrRestoreFileChunkAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/GetCcrRestoreFileChunkAction.java index 283d0b51f17bf..93cd6bd32dbab 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/GetCcrRestoreFileChunkAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/GetCcrRestoreFileChunkAction.java @@ -57,7 +57,13 @@ private TransportGetCcrRestoreFileChunkAction( ActionFilters actionFilters, CcrRestoreSourceService restoreSourceService ) { - super(actionName, transportService, actionFilters, GetCcrRestoreFileChunkRequest::new, ThreadPool.Names.GENERIC); + super( + actionName, + transportService, + actionFilters, + GetCcrRestoreFileChunkRequest::new, + transportService.getThreadPool().executor(ThreadPool.Names.GENERIC) + ); TransportActionProxy.registerProxyAction(transportService, actionName, false, GetCcrRestoreFileChunkResponse::new); this.restoreSourceService = restoreSourceService; this.bigArrays = bigArrays; diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/PutCcrRestoreSessionAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/PutCcrRestoreSessionAction.java index 0e50d18143f2f..1354c82a715d2 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/PutCcrRestoreSessionAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/repositories/PutCcrRestoreSessionAction.java @@ -71,7 +71,7 @@ private TransportPutCcrRestoreSessionAction( actionFilters, resolver, PutCcrRestoreSessionRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.indicesService = indicesService; this.ccrRestoreService = ccrRestoreService; diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckAction.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckAction.java index e6c93195bf1de..84552dab42b83 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckAction.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckAction.java @@ -70,7 +70,7 @@ public TransportNodeDeprecationCheckAction( actionFilters, NodesDeprecationCheckRequest::new, NodesDeprecationCheckAction.NodeRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.settings = settings; this.pluginsService = pluginsService; diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/TransportDeprecationCacheResetAction.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/TransportDeprecationCacheResetAction.java index b1ae442e97319..a73371a74b2f4 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/TransportDeprecationCacheResetAction.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/TransportDeprecationCacheResetAction.java @@ -50,7 +50,7 @@ public TransportDeprecationCacheResetAction( actionFilters, DeprecationCacheResetAction.Request::new, DeprecationCacheResetAction.NodeRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.rateLimitingFilterForIndexing = rateLimitingFilterForIndexing; } diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckActionTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckActionTests.java index 190ac8d388bcf..80692efb7474a 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckActionTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/TransportNodeDeprecationCheckActionTests.java @@ -19,26 +19,41 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.PluginsService; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.mockito.Mockito; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; public class TransportNodeDeprecationCheckActionTests extends ESTestCase { + private static ThreadPool threadPool; + + @Before + public void setup() { + threadPool = new TestThreadPool("TransportNodeDeprecationCheckActionTests"); + } + + @After + public void cleanup() { + ThreadPool.terminate(threadPool, 30, TimeUnit.SECONDS); + threadPool = null; + } + public void testNodeOperation() { Settings.Builder settingsBuilder = Settings.builder(); settingsBuilder.put("some.deprecated.property", "someValue1"); @@ -53,7 +68,6 @@ public void testNodeOperation() { settingsBuilder = Settings.builder(); settingsBuilder.put("some.bad.dynamic.property", "someValue1"); Settings dynamicSettings = settingsBuilder.build(); - ThreadPool threadPool = Mockito.mock(ThreadPool.class); final XPackLicenseState licenseState = null; Metadata metadata = Metadata.builder().transientSettings(dynamicSettings).build(); ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metadata(metadata).build(); @@ -64,11 +78,7 @@ public void testNodeOperation() { DiscoveryNode node = Mockito.mock(DiscoveryNode.class); when(node.getId()).thenReturn("mock-node"); TransportService transportService = Mockito.mock(TransportService.class); - - // TODO: temporary, remove in #97879 when(transportService.getThreadPool()).thenReturn(threadPool); - when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); - when(transportService.getLocalNode()).thenReturn(node); PluginsService pluginsService = Mockito.mock(PluginsService.class); ActionFilters actionFilters = Mockito.mock(ActionFilters.class); diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleIndexerAction.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleIndexerAction.java index 3fa32c9f86644..454f41c38d5c3 100644 --- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleIndexerAction.java +++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleIndexerAction.java @@ -69,7 +69,7 @@ public TransportDownsampleIndexerAction( indexNameExpressionResolver, DownsampleIndexerAction.Request::new, DownsampleIndexerAction.ShardDownsampleRequest::new, - Downsample.DOWSAMPLE_TASK_THREAD_POOL_NAME + transportService.getThreadPool().executor(Downsample.DOWSAMPLE_TASK_THREAD_POOL_NAME) ); this.client = new OriginSettingClient(client, ClientHelper.ROLLUP_ORIGIN); this.clusterService = clusterService; diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichCoordinatorStatsAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichCoordinatorStatsAction.java index 7b2bc64b62c98..f50995f9d5b4a 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichCoordinatorStatsAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichCoordinatorStatsAction.java @@ -147,7 +147,7 @@ public TransportAction( actionFilters, Request::new, NodeRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.enrichCache = enrichCache; this.coordinator = coordinator; diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java index 616b947b5cdd0..08cd87a78d874 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/EnrichShardMultiSearchAction.java @@ -194,7 +194,7 @@ public TransportAction( actionFilters, indexNameExpressionResolver, Request::new, - ThreadPool.Names.SEARCH + threadPool.executor(ThreadPool.Names.SEARCH) ); this.indicesService = indicesService; } diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlStatsAction.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlStatsAction.java index f54465781304a..90316d8b8db44 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlStatsAction.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlStatsAction.java @@ -49,7 +49,7 @@ public TransportEqlStatsAction( actionFilters, EqlStatsRequest::new, EqlStatsRequest.NodeStatsRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.planExecutor = planExecutor; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeService.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeService.java index 08e793e43c612..f6762e33bbc18 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeService.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeService.java @@ -60,8 +60,7 @@ public final class ExchangeService extends AbstractLifecycleComponent { private static final Logger LOGGER = LogManager.getLogger(ExchangeService.class); private final ThreadPool threadPool; - private final String requestExecutorName; - private final Executor responseExecutor; + private final Executor executor; private final Map sinks = ConcurrentCollections.newConcurrentMap(); private final Map sources = ConcurrentCollections.newConcurrentMap(); @@ -70,22 +69,16 @@ public final class ExchangeService extends AbstractLifecycleComponent { public ExchangeService(Settings settings, ThreadPool threadPool, String executorName) { this.threadPool = threadPool; - this.requestExecutorName = executorName; - this.responseExecutor = threadPool.executor(executorName); + this.executor = threadPool.executor(executorName); final var inactiveInterval = settings.getAsTime(INACTIVE_SINKS_INTERVAL_SETTING, TimeValue.timeValueMinutes(5)); - this.inactiveSinksReaper = new InactiveSinksReaper(LOGGER, threadPool, this.responseExecutor, inactiveInterval); + this.inactiveSinksReaper = new InactiveSinksReaper(LOGGER, threadPool, this.executor, inactiveInterval); } public void registerTransportHandler(TransportService transportService) { - transportService.registerRequestHandler( - EXCHANGE_ACTION_NAME, - threadPool.executor(requestExecutorName), - ExchangeRequest::new, - new ExchangeTransportAction() - ); + transportService.registerRequestHandler(EXCHANGE_ACTION_NAME, this.executor, ExchangeRequest::new, new ExchangeTransportAction()); transportService.registerRequestHandler( OPEN_EXCHANGE_ACTION_NAME, - threadPool.executor(requestExecutorName), + this.executor, OpenExchangeRequest::new, new OpenExchangeRequestHandler() ); @@ -257,7 +250,7 @@ protected void runInternal() { * @param remoteNode the node where the remote exchange sink is located */ public RemoteSink newRemoteSink(Task parentTask, String exchangeId, TransportService transportService, DiscoveryNode remoteNode) { - return new TransportRemoteSink(transportService, remoteNode, parentTask, exchangeId, responseExecutor); + return new TransportRemoteSink(transportService, remoteNode, parentTask, exchangeId, executor); } record TransportRemoteSink( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java index 6d859ef857bc7..fbaa812f68db7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.compute.operator.exchange.ExchangeService; import org.elasticsearch.search.SearchService; import org.elasticsearch.tasks.CancellableTask; @@ -59,7 +60,7 @@ public TransportEsqlQueryAction( BigArrays bigArrays ) { // TODO replace SAME when removing workaround for https://github.com/elastic/elasticsearch/issues/97916 - super(EsqlQueryAction.NAME, transportService, actionFilters, EsqlQueryRequest::new, ThreadPool.Names.SAME); + super(EsqlQueryAction.NAME, transportService, actionFilters, EsqlQueryRequest::new, EsExecutors.DIRECT_EXECUTOR_SERVICE); this.planExecutor = planExecutor; this.clusterService = clusterService; this.requestExecutor = threadPool.executor(EsqlPlugin.ESQL_THREAD_POOL_NAME); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlStatsAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlStatsAction.java index 356c00e52f919..cd708f3ae18ff 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlStatsAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlStatsAction.java @@ -52,7 +52,7 @@ public TransportEsqlStatsAction( actionFilters, EsqlStatsRequest::new, EsqlStatsRequest.NodeStatsRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.planExecutor = planExecutor; } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/EsqlInfoTransportActionTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/EsqlInfoTransportActionTests.java index f0b3a89a4444b..61e218572183c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/EsqlInfoTransportActionTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/EsqlInfoTransportActionTests.java @@ -49,11 +49,8 @@ public class EsqlInfoTransportActionTests extends ESTestCase { @Before public void init() { threadPool = new TestThreadPool(getTestName()); - - // TODO: temporary, remove in #97879 transportService = mock(TransportService.class); when(transportService.getThreadPool()).thenReturn(threadPool); - client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); } diff --git a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/GetGlobalCheckpointsShardAction.java b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/GetGlobalCheckpointsShardAction.java index e619f041fa866..8728c63d7df01 100644 --- a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/GetGlobalCheckpointsShardAction.java +++ b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/GetGlobalCheckpointsShardAction.java @@ -153,7 +153,7 @@ public TransportAction( actionFilters, indexNameExpressionResolver, Request::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.indicesService = indicesService; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportClearDeploymentCacheAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportClearDeploymentCacheAction.java index ac24036cc0b63..5ec1963ecec52 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportClearDeploymentCacheAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportClearDeploymentCacheAction.java @@ -15,9 +15,9 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.ClearDeploymentCacheAction; import org.elasticsearch.xpack.core.ml.action.ClearDeploymentCacheAction.Request; @@ -46,7 +46,7 @@ public TransportClearDeploymentCacheAction( Request::new, Response::new, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java index 62e09d67d95f5..178853ad4fa5b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -99,7 +100,7 @@ public TransportCloseJobAction( CloseJobAction.Request::new, CloseJobAction.Response::new, CloseJobAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.threadPool = threadPool; this.client = client; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataAction.java index 90f773a864183..f51498815c40e 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataAction.java @@ -50,6 +50,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; import java.util.stream.Collectors; @@ -60,7 +61,7 @@ public class TransportDeleteExpiredDataAction extends HandledTransportAction< private static final Logger logger = LogManager.getLogger(TransportDeleteExpiredDataAction.class); private final ThreadPool threadPool; - private final String executor; + private final Executor executor; private final OriginSettingClient client; private final ClusterService clusterService; private final Clock clock; @@ -81,7 +82,7 @@ public TransportDeleteExpiredDataAction( ) { this( threadPool, - MachineLearning.UTILITY_THREAD_POOL_NAME, + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME), transportService, actionFilters, client, @@ -95,7 +96,7 @@ public TransportDeleteExpiredDataAction( TransportDeleteExpiredDataAction( ThreadPool threadPool, - String executor, + Executor executor, TransportService transportService, ActionFilters actionFilters, Client client, @@ -207,11 +208,7 @@ void deleteExpiredData( // the chained calls must all run the ML utility thread pool NOT the thread // the previous action returned in which in the case of a transport_client_boss // thread is a disaster. - remover.remove( - requestsPerSecond, - new ThreadedActionListener<>(threadPool.executor(executor), nextListener), - isTimedOutSupplier - ); + remover.remove(requestsPerSecond, new ThreadedActionListener<>(executor, nextListener), isTimedOutSupplier); } else { if (haveAllPreviousDeletionsCompleted) { logger.info("Completed deletion of expired ML data"); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFlushJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFlushJobAction.java index 5814f5a0e7922..a5fe3ad67ca06 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFlushJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFlushJobAction.java @@ -10,8 +10,8 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.FlushJobAction; import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; @@ -35,7 +35,7 @@ public TransportFlushJobAction( actionFilters, FlushJobAction.Request::new, FlushJobAction.Response::new, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, processManager ); // ThreadPool.Names.SAME, because operations is executed by autodetect worker thread diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java index 792a821514a28..fa761c294a67b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java @@ -15,9 +15,9 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.common.notifications.AbstractAuditMessage; import org.elasticsearch.xpack.core.common.notifications.AbstractAuditor; @@ -73,7 +73,7 @@ public TransportForecastJobAction( ForecastJobAction.Request::new, ForecastJobAction.Response::new, // ThreadPool.Names.SAME, because operations is executed by autodetect worker thread - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, processManager ); this.jobResultsProvider = jobResultsProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDataFrameAnalyticsStatsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDataFrameAnalyticsStatsAction.java index e9478bc2462da..0f3d27cc196ed 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDataFrameAnalyticsStatsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDataFrameAnalyticsStatsAction.java @@ -98,7 +98,7 @@ public TransportGetDataFrameAnalyticsStatsAction( GetDataFrameAnalyticsStatsAction.Request::new, GetDataFrameAnalyticsStatsAction.Response::new, in -> new QueryPage<>(in, GetDataFrameAnalyticsStatsAction.Response.Stats::new), - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); this.client = client; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedRunningStateAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedRunningStateAction.java index 8879a6be36424..c9b85915a9fd6 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedRunningStateAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedRunningStateAction.java @@ -57,7 +57,7 @@ public TransportGetDatafeedRunningStateAction( Request::new, Response::new, Response::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDeploymentStatsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDeploymentStatsAction.java index d5d911e4329db..36d225a943348 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDeploymentStatsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDeploymentStatsAction.java @@ -66,7 +66,7 @@ public TransportGetDeploymentStatsAction( GetDeploymentStatsAction.Request::new, GetDeploymentStatsAction.Response::new, AssignmentStats::new, - ThreadPool.Names.MANAGEMENT + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsStatsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsStatsAction.java index fa63f5f9d78c1..666e6bf478429 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsStatsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsStatsAction.java @@ -84,7 +84,7 @@ public TransportGetJobsStatsAction( GetJobsStatsAction.Request::new, GetJobsStatsAction.Response::new, in -> new QueryPage<>(in, JobStats::new), - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.clusterService = clusterService; this.processManager = processManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java index 4c91f9e4913b3..f0f8287ab0d78 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java @@ -16,9 +16,9 @@ import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.InferTrainedModelDeploymentAction; import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; @@ -50,7 +50,7 @@ public TransportInferTrainedModelDeploymentAction( InferTrainedModelDeploymentAction.Request::new, InferTrainedModelDeploymentAction.Response::new, InferTrainedModelDeploymentAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportIsolateDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportIsolateDatafeedAction.java index 977ffbb65bd7a..4ceba52a7bd83 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportIsolateDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportIsolateDatafeedAction.java @@ -41,7 +41,7 @@ public TransportIsolateDatafeedAction(TransportService transportService, ActionF IsolateDatafeedAction.Request::new, IsolateDatafeedAction.Response::new, IsolateDatafeedAction.Response::new, - MachineLearning.UTILITY_THREAD_POOL_NAME + transportService.getThreadPool().executor(MachineLearning.UTILITY_THREAD_POOL_NAME) ); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportJobTaskAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportJobTaskAction.java index e337a3606926b..b9e908dc724b3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportJobTaskAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportJobTaskAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.xpack.ml.job.task.JobTask; import java.util.List; +import java.util.concurrent.Executor; /** * Base class that redirects a request to a node where the job task is running. @@ -42,7 +43,7 @@ public abstract class TransportJobTaskAction requestReader, Writeable.Reader responseReader, - String nodeExecutor, + Executor nodeExecutor, AutodetectProcessManager processManager ) { super(actionName, clusterService, transportService, actionFilters, requestReader, responseReader, responseReader, nodeExecutor); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportKillProcessAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportKillProcessAction.java index c64255dacd703..b79ba6a3cc46c 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportKillProcessAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportKillProcessAction.java @@ -58,7 +58,7 @@ public TransportKillProcessAction( KillProcessAction.Request::new, KillProcessAction.Response::new, KillProcessAction.Response::new, - MachineLearning.UTILITY_THREAD_POOL_NAME + transportService.getThreadPool().executor(MachineLearning.UTILITY_THREAD_POOL_NAME) ); this.auditor = auditor; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPersistJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPersistJobAction.java index dce719fec79ac..edea864eea779 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPersistJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPersistJobAction.java @@ -10,8 +10,8 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.PersistJobAction; import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; @@ -33,7 +33,7 @@ public TransportPersistJobAction( actionFilters, PersistJobAction.Request::new, PersistJobAction.Response::new, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, processManager ); // ThreadPool.Names.SAME, because operations is executed by autodetect worker thread diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPostDataAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPostDataAction.java index 4867f96b03224..7df6b35d272af 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPostDataAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPostDataAction.java @@ -10,9 +10,9 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.PostDataAction; import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; @@ -42,7 +42,7 @@ public TransportPostDataAction( actionFilters, PostDataAction.Request::new, PostDataAction.Response::new, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, processManager ); // ThreadPool.Names.SAME, because operations is executed by autodetect worker thread diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDataFrameAnalyticsAction.java index 477f5208f63de..f339376d97a03 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDataFrameAnalyticsAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.AtomicArray; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksService; @@ -90,7 +91,7 @@ public TransportStopDataFrameAnalyticsAction( StopDataFrameAnalyticsAction.Request::new, StopDataFrameAnalyticsAction.Response::new, StopDataFrameAnalyticsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.threadPool = threadPool; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java index 632b456a17a00..614fcfaed6120 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java @@ -94,7 +94,7 @@ public TransportStopDatafeedAction( StopDatafeedAction.Request::new, StopDatafeedAction.Response::new, StopDatafeedAction.Response::new, - MachineLearning.UTILITY_THREAD_POOL_NAME + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME) ); this.threadPool = Objects.requireNonNull(threadPool); this.persistentTasksService = Objects.requireNonNull(persistentTasksService); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopTrainedModelDeploymentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopTrainedModelDeploymentAction.java index c14c5d4aadf5b..6e90d097d1e9f 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopTrainedModelDeploymentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopTrainedModelDeploymentAction.java @@ -24,13 +24,13 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.StopTrainedModelDeploymentAction; @@ -89,7 +89,7 @@ public TransportStopTrainedModelDeploymentAction( StopTrainedModelDeploymentAction.Request::new, StopTrainedModelDeploymentAction.Response::new, StopTrainedModelDeploymentAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = new OriginSettingClient(client, ML_ORIGIN); this.ingestService = ingestService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportTrainedModelCacheInfoAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportTrainedModelCacheInfoAction.java index 15fc2c69d4820..0af653e1495a3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportTrainedModelCacheInfoAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportTrainedModelCacheInfoAction.java @@ -53,7 +53,7 @@ public TransportTrainedModelCacheInfoAction( actionFilters, TrainedModelCacheInfoAction.Request::new, NodeModelCacheInfoRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.modelLoadingService = modelLoadingService; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateProcessAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateProcessAction.java index 9fb72e96514c0..80846a74dfc61 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateProcessAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateProcessAction.java @@ -10,8 +10,8 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.UpdateProcessAction; import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; @@ -34,7 +34,7 @@ public TransportUpdateProcessAction( actionFilters, UpdateProcessAction.Request::new, UpdateProcessAction.Response::new, - ThreadPool.Names.SAME, + EsExecutors.DIRECT_EXECUTOR_SERVICE, processManager ); // ThreadPool.Names.SAME, because operations is executed by autodetect worker thread diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataActionTests.java index 26409b1ada849..124c6c6878ac9 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportDeleteExpiredDataActionTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.support.ActionTestUtils; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -38,7 +39,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; public class TransportDeleteExpiredDataActionTests extends ESTestCase { @@ -59,18 +59,13 @@ public void remove(float requestsPerSec, ActionListener listener, Boole @Before public void setup() { threadPool = new TestThreadPool("TransportDeleteExpiredDataActionTests thread pool"); - TransportService transportService = mock(TransportService.class); - - // TODO: temporary, remove in #97879 - when(transportService.getThreadPool()).thenReturn(threadPool); - Client client = mock(Client.class); ClusterService clusterService = mock(ClusterService.class); auditor = mock(AnomalyDetectionAuditor.class); transportDeleteExpiredDataAction = new TransportDeleteExpiredDataAction( threadPool, - ThreadPool.Names.SAME, - transportService, + EsExecutors.DIRECT_EXECUTOR_SERVICE, + mock(TransportService.class), new ActionFilters(Collections.emptySet()), client, clusterService, diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java index d2a694c04ae34..35a2b0c8cd58a 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringBulkActionTests.java @@ -24,13 +24,13 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskAwareRequest; import org.elasticsearch.tasks.TaskManager; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.MockUtils; import org.elasticsearch.test.RandomObjects; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -96,13 +96,8 @@ public void setUpMocks() { exporters = mock(Exporters.class); threadPool = mock(ThreadPool.class); clusterService = mock(ClusterService.class); - transportService = mock(TransportService.class); + transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); filters = mock(ActionFilters.class); - - // TODO: temporary, remove in #97879 - when(transportService.getThreadPool()).thenReturn(threadPool); - when(threadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); - when(transportService.getTaskManager()).thenReturn(taskManager); when(taskManager.register(anyString(), eq(MonitoringBulkAction.NAME), any(TaskAwareRequest.class))).thenReturn(mock(Task.class)); when(filters.filters()).thenReturn(new ActionFilter[0]); diff --git a/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportClearRepositoriesStatsArchiveAction.java b/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportClearRepositoriesStatsArchiveAction.java index b1a702c6c7a29..6872210ca303f 100644 --- a/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportClearRepositoriesStatsArchiveAction.java +++ b/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportClearRepositoriesStatsArchiveAction.java @@ -49,7 +49,7 @@ public TransportClearRepositoriesStatsArchiveAction( actionFilters, ClearRepositoriesMeteringArchiveRequest::new, ClearRepositoriesStatsArchiveNodeRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.repositoriesService = repositoriesService; } diff --git a/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportRepositoriesStatsAction.java b/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportRepositoriesStatsAction.java index 8c7f98592acd9..1574cce107416 100644 --- a/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportRepositoriesStatsAction.java +++ b/x-pack/plugin/repositories-metering-api/src/main/java/org/elasticsearch/xpack/repositories/metering/action/TransportRepositoriesStatsAction.java @@ -47,7 +47,7 @@ public TransportRepositoriesStatsAction( actionFilters, RepositoriesMeteringRequest::new, RepositoriesNodeStatsRequest::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.repositoriesService = repositoriesService; } diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportDeleteRollupJobAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportDeleteRollupJobAction.java index 971a51a042b98..86477f4f8c180 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportDeleteRollupJobAction.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportDeleteRollupJobAction.java @@ -17,11 +17,11 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.indexing.IndexerState; @@ -47,7 +47,7 @@ public TransportDeleteRollupJobAction(TransportService transportService, ActionF DeleteRollupJobAction.Request::new, DeleteRollupJobAction.Response::new, DeleteRollupJobAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportGetRollupCapsAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportGetRollupCapsAction.java index 672bd0495afbb..7a390a789d7d4 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportGetRollupCapsAction.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportGetRollupCapsAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -39,7 +40,13 @@ public class TransportGetRollupCapsAction extends HandledTransportAction request, - String executor, + Executor executor, IndicesService indicesService, XPackLicenseState licenseState ) { @@ -67,7 +68,7 @@ public abstract class AbstractTransportSearchableSnapshotsAction< ActionFilters actionFilters, IndexNameExpressionResolver resolver, Writeable.Reader request, - String executor, + Executor executor, IndicesService indicesService, XPackLicenseState licenseState, boolean canTripCircuitBreaker diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportClearSearchableSnapshotsCacheAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportClearSearchableSnapshotsCacheAction.java index 0fab5c4db2e46..8a4d21b4a98b8 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportClearSearchableSnapshotsCacheAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportClearSearchableSnapshotsCacheAction.java @@ -43,7 +43,7 @@ public TransportClearSearchableSnapshotsCacheAction( actionFilters, indexNameExpressionResolver, ClearSearchableSnapshotsCacheRequest::new, - ThreadPool.Names.MANAGEMENT, + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT), indicesService, licenseState, false diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportSearchableSnapshotsStatsAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportSearchableSnapshotsStatsAction.java index 3d518c4359b30..e4632b697f160 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportSearchableSnapshotsStatsAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportSearchableSnapshotsStatsAction.java @@ -48,7 +48,7 @@ public TransportSearchableSnapshotsStatsAction( actionFilters, indexNameExpressionResolver, SearchableSnapshotsStatsRequest::new, - ThreadPool.Names.MANAGEMENT, + transportService.getThreadPool().executor(ThreadPool.Names.MANAGEMENT), indicesService, licenseState ); diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotCacheStoresAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotCacheStoresAction.java index 479d7eb0f3430..ce49969d3fb6c 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotCacheStoresAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotCacheStoresAction.java @@ -62,7 +62,7 @@ public TransportSearchableSnapshotCacheStoresAction( actionFilters, Request::new, NodeRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.cacheService = cacheService.get(); } diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotsNodeCachesStatsAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotsNodeCachesStatsAction.java index 9a12661cc2a67..71aa7709685b1 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotsNodeCachesStatsAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/cache/TransportSearchableSnapshotsNodeCachesStatsAction.java @@ -72,7 +72,7 @@ public TransportSearchableSnapshotsNodeCachesStatsAction( actionFilters, NodesRequest::new, NodeRequest::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.frozenCacheService = frozenCacheService; this.licenseState = licenseState; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/TransportClearSecurityCacheAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/TransportClearSecurityCacheAction.java index d33e10e6d32f3..56c8ac6a80868 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/TransportClearSecurityCacheAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/TransportClearSecurityCacheAction.java @@ -53,7 +53,7 @@ public TransportClearSecurityCacheAction( actionFilters, ClearSecurityCacheRequest::new, ClearSecurityCacheRequest.Node::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.cacheInvalidatorRegistry = cacheInvalidatorRegistry; } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/privilege/TransportClearPrivilegesCacheAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/privilege/TransportClearPrivilegesCacheAction.java index bd68ea31d9df6..bfaac8c84ec34 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/privilege/TransportClearPrivilegesCacheAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/privilege/TransportClearPrivilegesCacheAction.java @@ -52,7 +52,7 @@ public TransportClearPrivilegesCacheAction( actionFilters, ClearPrivilegesCacheRequest::new, ClearPrivilegesCacheRequest.Node::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.rolesStore = rolesStore; this.cacheInvalidatorRegistry = cacheInvalidatorRegistry; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheAction.java index 49d2e3e74b2c4..3daf9c4053bfa 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheAction.java @@ -54,7 +54,7 @@ public TransportClearRealmCacheAction( actionFilters, ClearRealmCacheRequest::new, ClearRealmCacheRequest.Node::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.realms = realms; this.authenticationService = authenticationService; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/role/TransportClearRolesCacheAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/role/TransportClearRolesCacheAction.java index adcd1c4090b6f..7ebec117ff428 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/role/TransportClearRolesCacheAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/role/TransportClearRolesCacheAction.java @@ -48,7 +48,7 @@ public TransportClearRolesCacheAction( actionFilters, ClearRolesCacheRequest::new, ClearRolesCacheRequest.Node::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.rolesStore = rolesStore; } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java index 2e41fc622cb77..8c338250f16a0 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java @@ -55,7 +55,7 @@ public TransportGetServiceAccountNodesCredentialsAction( actionFilters, GetServiceAccountCredentialsNodesRequest::new, GetServiceAccountCredentialsNodesRequest.Node::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.fileServiceAccountTokenStore = fileServiceAccountTokenStore; } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/apikey/TransportGrantApiKeyActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/apikey/TransportGrantApiKeyActionTests.java index f9e219f168718..c8c996f37ebfc 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/apikey/TransportGrantApiKeyActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/apikey/TransportGrantApiKeyActionTests.java @@ -82,8 +82,6 @@ public void setupMocks() throws Exception { threadPool = new TestThreadPool("TP-" + getTestName()); final ThreadContext threadContext = threadPool.getThreadContext(); - - // TODO: temporary, remove in #97879 TransportService transportService = mock(TransportService.class); when(transportService.getThreadPool()).thenReturn(threadPool); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/profile/TransportProfileHasPrivilegesActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/profile/TransportProfileHasPrivilegesActionTests.java index dca8633d7c1eb..4155e6d4168cb 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/profile/TransportProfileHasPrivilegesActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/profile/TransportProfileHasPrivilegesActionTests.java @@ -81,10 +81,7 @@ public class TransportProfileHasPrivilegesActionTests extends ESTestCase { public void setup() { threadPool = new TestThreadPool(TransportProfileHasPrivilegesActionTests.class.getSimpleName()); transportService = mock(TransportService.class); - - // TODO: temporary, remove in #97879 when(transportService.getThreadPool()).thenReturn(threadPool); - actionFilters = mock(ActionFilters.class); authorizationService = mock(AuthorizationService.class); nativePrivilegeStore = mock(NativePrivilegeStore.class); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheActionTests.java index 92bef206a9d6a..8a1d6bfc069d4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/realm/TransportClearRealmCacheActionTests.java @@ -14,7 +14,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.MockUtils; +import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.security.action.realm.ClearRealmCacheRequest; @@ -24,11 +24,13 @@ import org.elasticsearch.xpack.core.security.authc.support.CachingRealm; import org.elasticsearch.xpack.security.authc.AuthenticationService; import org.elasticsearch.xpack.security.authc.Realms; +import org.junit.After; import org.junit.Before; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +41,7 @@ public class TransportClearRealmCacheActionTests extends ESTestCase { + private static ThreadPool threadPool; private AuthenticationService authenticationService; private TransportClearRealmCacheAction action; private TestCachingRealm nativeRealm; @@ -46,13 +49,14 @@ public class TransportClearRealmCacheActionTests extends ESTestCase { @Before public void setup() { + threadPool = new TestThreadPool("TransportClearRealmCacheActionTests"); + authenticationService = mock(AuthenticationService.class); nativeRealm = mockRealm("native"); fileRealm = mockRealm("file"); final Realms realms = mockRealms(List.of(nativeRealm, fileRealm)); - - ThreadPool threadPool = mock(ThreadPool.class); - TransportService transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); + TransportService transportService = mock(TransportService.class); + when(transportService.getThreadPool()).thenReturn(threadPool); action = new TransportClearRealmCacheAction( threadPool, @@ -64,6 +68,12 @@ public void setup() { ); } + @After + public void cleanup() { + ThreadPool.terminate(threadPool, 30, TimeUnit.SECONDS); + threadPool = null; + } + public void testSingleUserCacheCleanupForAllRealms() { final String user = "test"; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportCreateTokenActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportCreateTokenActionTests.java index dcdeace6b06b6..fe48408b39c54 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportCreateTokenActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportCreateTokenActionTests.java @@ -100,11 +100,7 @@ public class TransportCreateTokenActionTests extends ESTestCase { @Before public void setupClient() { threadPool = new TestThreadPool(getTestName()); - - // TODO: temporary, remove in #97879 transportService = mock(TransportService.class); - when(transportService.getThreadPool()).thenReturn(threadPool); - client = mock(Client.class); idxReqReference = new AtomicReference<>(); authenticationService = mock(AuthenticationService.class); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportInvalidateTokenActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportInvalidateTokenActionTests.java index f07bb7dc365ae..a0f7892c3319d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportInvalidateTokenActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/token/TransportInvalidateTokenActionTests.java @@ -66,11 +66,7 @@ public class TransportInvalidateTokenActionTests extends ESTestCase { @Before public void setup() { threadPool = new TestThreadPool(getTestName()); - - // TODO: temporary, remove in #97879 transportService = mock(TransportService.class); - when(transportService.getThreadPool()).thenReturn(threadPool); - securityContext = new SecurityContext(Settings.EMPTY, threadPool.getThreadContext()); client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java index e5ebe47d177ff..5c9e0fc0b9202 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java @@ -167,7 +167,13 @@ public static class TransportAction extends HandledTransportAction Date: Thu, 21 Sep 2023 15:47:05 -0400 Subject: [PATCH 024/155] [ML] Adding retries for starting model deployment (#99673) * Reducing chunk size and adding retries * Testing search part * Update docs/changelog/99673.yaml * Unwrapping exception * Updating changelog * Wrapping exception and tests * Adding comments --- docs/changelog/99673.yaml | 5 + .../TrainedModelAssignmentNodeService.java | 8 + .../ChunkedTrainedModelRestorer.java | 57 +++++- .../ChunkedTrainedModelRestorerTests.java | 171 ++++++++++++++++++ 4 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/99673.yaml create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorerTests.java diff --git a/docs/changelog/99673.yaml b/docs/changelog/99673.yaml new file mode 100644 index 0000000000000..b48d620b21f49 --- /dev/null +++ b/docs/changelog/99673.yaml @@ -0,0 +1,5 @@ +pr: 99673 +summary: Adding retry logic for start model deployment API +area: Machine Learning +type: bug +issues: [ ] diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java index f1b9298b05ca4..e31f8d9992b64 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java @@ -217,8 +217,16 @@ void loadQueuedModels() { logger.debug(() -> "[" + deploymentId + "] Start deployment failed as model [" + modelId + "] was not found", ex); handleLoadFailure(loadingTask, ExceptionsHelper.missingTrainedModel(modelId, ex)); } else if (ExceptionsHelper.unwrapCause(ex) instanceof SearchPhaseExecutionException) { + /* + * This case will not catch the ElasticsearchException generated from the ChunkedTrainedModelRestorer in a scenario + * where the maximum number of retries for a SearchPhaseExecutionException or CBE occur. This is intentional. If the + * retry logic fails after retrying we should return the error and not retry here. The generated + * ElasticsearchException will contain the SearchPhaseExecutionException or CBE but cannot be unwrapped. + */ logger.debug(() -> "[" + deploymentId + "] Start deployment failed, will retry", ex); // A search phase execution failure should be retried, push task back to the queue + + // This will cause the entire model to be reloaded (all the chunks) loadingToRetry.add(loadingTask); } else { handleLoadFailure(loadingTask, ex); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorer.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorer.java index 2ba81386fa754..015b88552a1d0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorer.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorer.java @@ -9,16 +9,20 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.OriginSettingClient; +import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.sort.SortBuilders; @@ -36,6 +40,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static org.elasticsearch.core.Strings.format; @@ -58,6 +63,8 @@ public class ChunkedTrainedModelRestorer { private static final Logger logger = LogManager.getLogger(ChunkedTrainedModelRestorer.class); private static final int MAX_NUM_DEFINITION_DOCS = 20; + private static final int SEARCH_RETRY_LIMIT = 5; + private static final TimeValue SEARCH_FAILURE_RETRY_WAIT_TIME = new TimeValue(5, TimeUnit.SECONDS); private final Client client; private final NamedXContentRegistry xContentRegistry; @@ -142,7 +149,14 @@ private void doSearch( UTILITY_THREAD_POOL_NAME, Thread.currentThread().getName() ); - SearchResponse searchResponse = client.search(searchRequest).actionGet(); + + SearchResponse searchResponse = retryingSearch( + client, + modelId, + searchRequest, + SEARCH_RETRY_LIMIT, + SEARCH_FAILURE_RETRY_WAIT_TIME + ); if (searchResponse.getHits().getHits().length == 0) { errorConsumer.accept(new ResourceNotFoundException(Messages.getMessage(Messages.MODEL_DEFINITION_NOT_FOUND, modelId))); return; @@ -201,6 +215,47 @@ private void doSearch( } } + static SearchResponse retryingSearch(Client client, String modelId, SearchRequest searchRequest, int retries, TimeValue sleep) + throws InterruptedException { + int failureCount = 0; + + while (true) { + try { + return client.search(searchRequest).actionGet(); + } catch (Exception e) { + if (ExceptionsHelper.unwrapCause(e) instanceof SearchPhaseExecutionException == false + && ExceptionsHelper.unwrapCause(e) instanceof CircuitBreakingException == false) { + throw e; + } + + if (failureCount >= retries) { + logger.warn(format("[%s] searching for model part failed %s times, returning failure", modelId, retries)); + /* + * ElasticsearchException does not implement the ElasticsearchWrapperException interface so this exception cannot + * be unwrapped. This is important because the TrainedModelAssignmentNodeService has retry logic when a + * SearchPhaseExecutionException occurs: + * https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java#L219 + * This intentionally prevents that code from attempting to retry loading the entire model. If the retry logic here + * fails after the set retries we should not retry loading the entire model to avoid additional strain on the cluster. + */ + throw new ElasticsearchException( + format( + "loading model [%s] failed after [%s] retries. The deployment is now in a failed state, " + + "the error may be transient please stop the deployment and restart", + modelId, + retries + ), + e + ); + } + + failureCount++; + logger.debug(format("[%s] searching for model part failed %s times, retrying", modelId, failureCount)); + TimeUnit.SECONDS.sleep(sleep.getSeconds()); + } + } + } + private static SearchRequestBuilder buildSearchBuilder(Client client, String modelId, String index, int searchSize) { return client.prepareSearch(index) .setQuery( diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorerTests.java new file mode 100644 index 0000000000000..abdd1def956f0 --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/persistence/ChunkedTrainedModelRestorerTests.java @@ -0,0 +1,171 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.inference.persistence; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.action.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.ShardSearchFailure; +import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.test.ESTestCase; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ChunkedTrainedModelRestorerTests extends ESTestCase { + public void testRetryingSearch_ReturnsSearchResults() throws InterruptedException { + var mockClient = mock(Client.class); + var mockSearchResponse = mock(SearchResponse.class, RETURNS_DEEP_STUBS); + + PlainActionFuture searchFuture = new PlainActionFuture<>(); + searchFuture.onResponse(mockSearchResponse); + when(mockClient.search(any())).thenReturn(searchFuture); + + var request = createSearchRequest(); + + assertThat( + ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 5, new TimeValue(1, TimeUnit.NANOSECONDS)), + is(mockSearchResponse) + ); + + verify(mockClient, times(1)).search(any()); + } + + public void testRetryingSearch_ThrowsSearchPhaseExceptionWithNoRetries() { + try (var mockClient = mock(Client.class)) { + var searchPhaseException = new SearchPhaseExecutionException("phase", "error", ShardSearchFailure.EMPTY_ARRAY); + when(mockClient.search(any())).thenThrow(searchPhaseException); + + var request = createSearchRequest(); + + ElasticsearchException exception = expectThrows( + ElasticsearchException.class, + () -> ChunkedTrainedModelRestorer.retryingSearch(mockClient, "1", request, 0, new TimeValue(1, TimeUnit.NANOSECONDS)) + ); + + assertThat(exception.getCause(), is(searchPhaseException)); + assertThat( + exception.getMessage(), + is( + "loading model [1] failed after [0] retries. The deployment is now in a failed state, the error may be " + + "transient please stop the deployment and restart" + ) + ); + verify(mockClient, times(1)).search(any()); + } + } + + public void testRetryingSearch_ThrowsSearchPhaseExceptionAfterOneRetry() { + try (var mockClient = mock(Client.class)) { + var searchPhaseException = new SearchPhaseExecutionException("phase", "error", ShardSearchFailure.EMPTY_ARRAY); + when(mockClient.search(any())).thenThrow(searchPhaseException); + + var request = createSearchRequest(); + + ElasticsearchException exception = expectThrows( + ElasticsearchException.class, + () -> ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 1, new TimeValue(1, TimeUnit.NANOSECONDS)) + ); + + assertThat(exception.getCause(), is(searchPhaseException)); + verify(mockClient, times(2)).search(any()); + } + } + + public void testRetryingSearch_ThrowsCircuitBreakingExceptionAfterOneRetry_FromSearchPhaseException() { + try (var mockClient = mock(Client.class)) { + var searchPhaseException = new SearchPhaseExecutionException("phase", "error", ShardSearchFailure.EMPTY_ARRAY); + var circuitBreakerException = new CircuitBreakingException("error", CircuitBreaker.Durability.TRANSIENT); + when(mockClient.search(any())).thenThrow(searchPhaseException).thenThrow(circuitBreakerException); + + var request = createSearchRequest(); + + ElasticsearchException exception = expectThrows( + ElasticsearchException.class, + () -> ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 1, new TimeValue(1, TimeUnit.NANOSECONDS)) + ); + + assertThat(exception.getCause(), is(circuitBreakerException)); + verify(mockClient, times(2)).search(any()); + } + } + + public void testRetryingSearch_EnsureExceptionCannotBeUnwrapped() { + try (var mockClient = mock(Client.class)) { + var searchPhaseExecutionException = new SearchPhaseExecutionException("phase", "error", ShardSearchFailure.EMPTY_ARRAY); + when(mockClient.search(any())).thenThrow(searchPhaseExecutionException); + + var request = createSearchRequest(); + + ElasticsearchException exception = expectThrows( + ElasticsearchException.class, + () -> ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 1, new TimeValue(1, TimeUnit.NANOSECONDS)) + ); + + assertThat(ExceptionsHelper.unwrapCause(exception), is(exception)); + assertThat(ExceptionsHelper.unwrapCause(exception), instanceOf(ElasticsearchException.class)); + verify(mockClient, times(2)).search(any()); + } + } + + public void testRetryingSearch_ThrowsIllegalArgumentExceptionIgnoringRetries() { + try (var mockClient = mock(Client.class)) { + var exception = new IllegalArgumentException("Error"); + when(mockClient.search(any())).thenThrow(exception); + + var request = createSearchRequest(); + + IllegalArgumentException thrownException = expectThrows( + IllegalArgumentException.class, + () -> ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 1, new TimeValue(1, TimeUnit.NANOSECONDS)) + ); + + assertThat(thrownException, is(exception)); + verify(mockClient, times(1)).search(any()); + } + } + + public void testRetryingSearch_ThrowsSearchPhaseExceptionOnce_ThenReturnsResponse() throws InterruptedException { + try (var mockClient = mock(Client.class)) { + var mockSearchResponse = mock(SearchResponse.class, RETURNS_DEEP_STUBS); + + PlainActionFuture searchFuture = new PlainActionFuture<>(); + searchFuture.onResponse(mockSearchResponse); + + var searchPhaseException = new SearchPhaseExecutionException("phase", "error", ShardSearchFailure.EMPTY_ARRAY); + when(mockClient.search(any())).thenThrow(searchPhaseException).thenReturn(searchFuture); + + var request = createSearchRequest(); + + assertThat( + ChunkedTrainedModelRestorer.retryingSearch(mockClient, "", request, 1, new TimeValue(1, TimeUnit.NANOSECONDS)), + is(mockSearchResponse) + ); + + verify(mockClient, times(2)).search(any()); + } + } + + private static SearchRequest createSearchRequest() { + return new SearchRequest("index"); + } +} From fcf107a044ece838b7cfc552aa3dd1f108d7da5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Thu, 21 Sep 2023 22:26:20 +0200 Subject: [PATCH 025/155] [Transform] Ignore "index not found" error when `delete_dest_index` flag is set but the dest index doesn't exist (#99738) --- docs/changelog/99738.yaml | 6 ++++++ .../integration/TransformDeleteIT.java | 21 ++++++++++++++++--- .../integration/TransformRestTestCase.java | 9 +++++--- .../TransportDeleteTransformAction.java | 12 ++++++++++- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 docs/changelog/99738.yaml diff --git a/docs/changelog/99738.yaml b/docs/changelog/99738.yaml new file mode 100644 index 0000000000000..1b65926aed741 --- /dev/null +++ b/docs/changelog/99738.yaml @@ -0,0 +1,6 @@ +pr: 99738 +summary: Ignore "index not found" error when `delete_dest_index` flag is set but the + dest index doesn't exist +area: Transform +type: bug +issues: [] diff --git a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDeleteIT.java b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDeleteIT.java index 31e8ddd97db06..bb68c7b84da5d 100644 --- a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDeleteIT.java +++ b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDeleteIT.java @@ -108,7 +108,7 @@ public void testDeleteWithParamDeletesAutoCreatedDestinationIndex() throws Excep assertTrue(indexExists(transformDest)); assertTrue(aliasExists(transformDestAlias)); - deleteTransform(transformId, true); + deleteTransform(transformId, false, true); assertFalse(indexExists(transformDest)); assertFalse(aliasExists(transformDest)); } @@ -134,7 +134,7 @@ public void testDeleteWithParamDeletesManuallyCreatedDestinationIndex() throws E assertTrue(indexExists(transformDest)); assertTrue(aliasExists(transformDestAlias)); - deleteTransform(transformId, true); + deleteTransform(transformId, false, true); assertFalse(indexExists(transformDest)); assertFalse(aliasExists(transformDestAlias)); } @@ -158,7 +158,7 @@ public void testDeleteWithParamDoesNotDeleteManuallySetUpAlias() throws Exceptio assertTrue(indexExists(transformDest)); assertTrue(aliasExists(transformDestAlias)); - ResponseException e = expectThrows(ResponseException.class, () -> deleteTransform(transformId, true)); + ResponseException e = expectThrows(ResponseException.class, () -> deleteTransform(transformId, false, true)); assertThat( e.getMessage(), containsString( @@ -170,6 +170,21 @@ public void testDeleteWithParamDoesNotDeleteManuallySetUpAlias() throws Exceptio ); } + public void testDeleteDestinationIndexIsNoOpWhenNoDestinationIndexExists() throws Exception { + String transformId = "transform-5"; + String transformDest = transformId + "_idx"; + String transformDestAlias = transformId + "_alias"; + setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME, transformDest, transformDestAlias); + + createTransform(transformId, transformDest, transformDestAlias); + assertFalse(indexExists(transformDest)); + assertFalse(aliasExists(transformDestAlias)); + + deleteTransform(transformId, false, true); + assertFalse(indexExists(transformDest)); + assertFalse(aliasExists(transformDestAlias)); + } + private void createTransform(String transformId, String destIndex, String destAlias) throws IOException { final Request createTransformRequest = createRequestWithAuth( "PUT", diff --git a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformRestTestCase.java b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformRestTestCase.java index 77f7ecb273483..e6388bb6fea5d 100644 --- a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformRestTestCase.java +++ b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformRestTestCase.java @@ -611,12 +611,15 @@ protected static Map getTransformsStateAndStats(int from, int si } protected static void deleteTransform(String transformId) throws IOException { - deleteTransform(transformId, false); + // Ignore 404s because they imply someone was racing us to delete this transform. + deleteTransform(transformId, true, false); } - protected static void deleteTransform(String transformId, boolean deleteDestIndex) throws IOException { + protected static void deleteTransform(String transformId, boolean ignoreNotFound, boolean deleteDestIndex) throws IOException { Request request = new Request("DELETE", getTransformEndpoint() + transformId); - request.addParameter("ignore", "404"); // Ignore 404s because they imply someone was racing us to delete this + if (ignoreNotFound) { + request.addParameter("ignore", "404"); + } if (deleteDestIndex) { request.addParameter(TransformField.DELETE_DEST_INDEX.getPreferredName(), Boolean.TRUE.toString()); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java index 52a2bfc011194..9e94fbb3e5de9 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -135,6 +136,15 @@ private void deleteDestinationIndex( TimeValue timeout, ActionListener listener ) { + // <3> Check if the error is "index not found" error. If so, just move on. The index is already deleted. + ActionListener deleteDestIndexListener = ActionListener.wrap(listener::onResponse, e -> { + if (e instanceof IndexNotFoundException) { + listener.onResponse(AcknowledgedResponse.TRUE); + } else { + listener.onFailure(e); + } + }); + // <2> Delete destination index ActionListener> getTransformConfigurationListener = ActionListener.wrap( transformConfigAndVersion -> { @@ -149,7 +159,7 @@ private void deleteDestinationIndex( client, DeleteIndexAction.INSTANCE, deleteDestIndexRequest, - listener + deleteDestIndexListener ); }, listener::onFailure From 571cebc4155d4e20fdc3889b782258d9826fd8ef Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 21 Sep 2023 23:49:04 -0400 Subject: [PATCH 026/155] Warn about third-party JVM agents in docs (#99718) * Warn about third-party JVM agents in docs * Reword, and move warning to its own section Co-authored-by: James Rodewig --- docs/reference/setup/install.asciidoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/reference/setup/install.asciidoc b/docs/reference/setup/install.asciidoc index 9d47e0e80bb47..858902bb72ef2 100644 --- a/docs/reference/setup/install.asciidoc +++ b/docs/reference/setup/install.asciidoc @@ -90,6 +90,14 @@ the bundled JVM are treated as if they were within {es} itself. The bundled JVM is located within the `jdk` subdirectory of the {es} home directory. You may remove this directory if using your own JVM. +[discrete] +[[jvm-agents]] +=== JVM and Java agents + +Don't use third-party Java agents that attach to the JVM. These agents +can reduce {es} performance, including freezing or crashing nodes. + + include::install/targz.asciidoc[] include::install/zip-windows.asciidoc[] From dc8d84d2d53660923a27afea33d6f0779ccdc634 Mon Sep 17 00:00:00 2001 From: Mary Gouseti Date: Fri, 22 Sep 2023 10:51:24 +0300 Subject: [PATCH 027/155] Apply mixed tests after Data Stream Lifecycle went GA (#99795) --- .../test/indices.get_index_template/10_basic.yml | 12 ++++++------ .../indices.simulate_index_template/10_basic.yml | 4 ++-- .../test/indices.simulate_template/10_basic.yml | 4 ++-- .../resources/rest-api-spec/test/dlm/10_usage.yml | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get_index_template/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get_index_template/10_basic.yml index 2c4339d8c9b9a..41e5506c412cd 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get_index_template/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get_index_template/10_basic.yml @@ -107,8 +107,8 @@ setup: --- "Add data stream lifecycle": - skip: - version: " - 8.9.99" - reason: "Data stream lifecycle in index templates was updated after 8.9" + version: " - 8.10.99" + reason: "Data stream lifecycle in index templates was updated after 8.10" features: allowed_warnings - do: @@ -142,8 +142,8 @@ setup: --- "Get data stream lifecycle with default rollover": - skip: - version: " - 8.9.99" - reason: "Data stream lifecycle in index templates was updated after 8.9" + version: " - 8.10.99" + reason: "Data stream lifecycle in index templates was updated after 8.10" features: allowed_warnings - do: @@ -171,8 +171,8 @@ setup: --- "Reject data stream lifecycle without data stream configuration": - skip: - version: " - 8.9.99" - reason: "Data stream lifecycle in index templates was updated after 8.9" + version: " - 8.10.99" + reason: "Data stream lifecycle in index templates was updated after 8.10" - do: catch: bad_request indices.put_index_template: diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_index_template/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_index_template/10_basic.yml index fffb7e10e68cb..7256c1736ebd9 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_index_template/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_index_template/10_basic.yml @@ -227,8 +227,8 @@ --- "Simulate index template with lifecycle and include defaults": - skip: - version: " - 8.9.99" - reason: "Lifecycle is only available in 8.10+" + version: " - 8.10.99" + reason: "Lifecycle is only available in 8.11+" features: ["default_shards"] - do: diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_template/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_template/10_basic.yml index b5b1de1bcd106..887a2bc2ce705 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_template/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.simulate_template/10_basic.yml @@ -202,8 +202,8 @@ --- "Simulate template with lifecycle and include defaults": - skip: - version: " - 8.9.99" - reason: "Lifecycle is only available in 8.10+" + version: " - 8.10.99" + reason: "Lifecycle is only available in 8.11+" features: ["default_shards"] - do: diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/dlm/10_usage.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/dlm/10_usage.yml index 8524f39b5d2cd..3033c83af8e33 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/dlm/10_usage.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/dlm/10_usage.yml @@ -1,8 +1,8 @@ --- "Test data stream lifecycle usage stats": - skip: - version: "- 8.9.99" - reason: "the data stream lifecycle stats were updated to the usage api in 8.10" + version: "- 8.10.99" + reason: "the data stream lifecycle stats were updated to the usage api in 8.11" features: allowed_warnings - do: From 2df6def54fbff76a1d0108cbef7d9dca46757741 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Fri, 22 Sep 2023 11:20:16 +0200 Subject: [PATCH 028/155] Dry up + cleanup bits of dynamic mapping parsing (#99697) Just a few obvious dry-up spots and some dead code removal. --- .../index/mapper/DocumentParser.java | 87 +++++++++---------- .../index/mapper/NestedObjectMapper.java | 18 +--- .../index/mapper/ObjectMapper.java | 10 ++- 3 files changed, 51 insertions(+), 64 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 03653322c383c..616c48a536eef 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -398,8 +398,7 @@ private static void addFields(IndexVersion indexCreatedVersion, LuceneDocument n static void parseObjectOrField(DocumentParserContext context, Mapper mapper) throws IOException { if (mapper instanceof ObjectMapper objectMapper) { - context = context.createChildContext(objectMapper); - parseObjectOrNested(context); + parseObjectOrNested(context.createChildContext(objectMapper)); } else if (mapper instanceof FieldMapper fieldMapper) { if (shouldFlattenObject(context, fieldMapper)) { // we pass the mapper's simpleName as parentName to the new DocumentParserContext @@ -462,24 +461,25 @@ private static void parseObject(final DocumentParserContext context, String curr assert currentFieldName != null; Mapper objectMapper = context.getMapper(currentFieldName); if (objectMapper != null) { - context.path().add(currentFieldName); - if (objectMapper instanceof ObjectMapper objMapper) { - if (objMapper.subobjects() == false) { - context.path().setWithinLeafObject(true); - } - } - parseObjectOrField(context, objectMapper); - context.path().setWithinLeafObject(false); - context.path().remove(); + doParseObject(context, currentFieldName, objectMapper); } else { parseObjectDynamic(context, currentFieldName); } } + private static void doParseObject(DocumentParserContext context, String currentFieldName, Mapper objectMapper) throws IOException { + context.path().add(currentFieldName); + if (objectMapper instanceof ObjectMapper objMapper && objMapper.subobjects() == false) { + context.path().setWithinLeafObject(true); + } + parseObjectOrField(context, objectMapper); + context.path().setWithinLeafObject(false); + context.path().remove(); + } + private static void parseObjectDynamic(DocumentParserContext context, String currentFieldName) throws IOException { - if (context.dynamic() == ObjectMapper.Dynamic.STRICT) { - throw new StrictDynamicMappingException(context.parser().getTokenLocation(), context.parent().fullPath(), currentFieldName); - } else if (context.dynamic() == ObjectMapper.Dynamic.FALSE) { + ensureNotStrict(context, currentFieldName); + if (context.dynamic() == ObjectMapper.Dynamic.FALSE) { failIfMatchesRoutingPath(context, currentFieldName); // not dynamic, read everything up to end object context.parser().skipChildren(); @@ -525,15 +525,7 @@ private static void parseObjectDynamic(DocumentParserContext context, String cur if (dynamicObjectMapper instanceof NestedObjectMapper && context.isWithinCopyTo()) { throwOnCreateDynamicNestedViaCopyTo(dynamicObjectMapper, context); } - context.path().add(currentFieldName); - if (dynamicObjectMapper instanceof ObjectMapper objectMapper) { - if (objectMapper.subobjects() == false) { - context.path().setWithinLeafObject(true); - } - } - parseObjectOrField(context, dynamicObjectMapper); - context.path().setWithinLeafObject(false); - context.path().remove(); + doParseObject(context, currentFieldName, dynamicObjectMapper); } } @@ -556,24 +548,27 @@ private static void parseArray(DocumentParserContext context, String lastFieldNa parseNonDynamicArray(context, lastFieldName, lastFieldName); } } else { - if (context.dynamic() == ObjectMapper.Dynamic.STRICT) { - throw new StrictDynamicMappingException(context.parser().getTokenLocation(), context.parent().fullPath(), lastFieldName); - } else if (context.dynamic() == ObjectMapper.Dynamic.FALSE) { - context.parser().skipChildren(); + parseArrayDynamic(context, lastFieldName); + } + } + + private static void parseArrayDynamic(DocumentParserContext context, String currentFieldName) throws IOException { + ensureNotStrict(context, currentFieldName); + if (context.dynamic() == ObjectMapper.Dynamic.FALSE) { + context.parser().skipChildren(); + } else { + Mapper.Builder objectBuilderFromTemplate = DynamicFieldsBuilder.findTemplateBuilderForObject(context, currentFieldName); + if (objectBuilderFromTemplate == null) { + parseNonDynamicArray(context, currentFieldName, currentFieldName); } else { - Mapper.Builder objectBuilderFromTemplate = DynamicFieldsBuilder.findTemplateBuilderForObject(context, lastFieldName); - if (objectBuilderFromTemplate == null) { - parseNonDynamicArray(context, lastFieldName, lastFieldName); + Mapper objectMapperFromTemplate = objectBuilderFromTemplate.build(context.createDynamicMapperBuilderContext()); + if (parsesArrayValue(objectMapperFromTemplate)) { + context.addDynamicMapper(objectMapperFromTemplate.name(), objectBuilderFromTemplate); + context.path().add(currentFieldName); + parseObjectOrField(context, objectMapperFromTemplate); + context.path().remove(); } else { - Mapper objectMapperFromTemplate = objectBuilderFromTemplate.build(context.createDynamicMapperBuilderContext()); - if (parsesArrayValue(objectMapperFromTemplate)) { - context.addDynamicMapper(objectMapperFromTemplate.name(), objectBuilderFromTemplate); - context.path().add(lastFieldName); - parseObjectOrField(context, objectMapperFromTemplate); - context.path().remove(); - } else { - parseNonDynamicArray(context, lastFieldName, lastFieldName); - } + parseNonDynamicArray(context, currentFieldName, currentFieldName); } } } @@ -671,15 +666,13 @@ private static void parseNullValue(DocumentParserContext context, String lastFie if (mapper != null) { // TODO: passing null to an object seems bogus? parseObjectOrField(context, mapper); - } else if (context.dynamic() == ObjectMapper.Dynamic.STRICT) { - throw new StrictDynamicMappingException(context.parser().getTokenLocation(), context.parent().fullPath(), lastFieldName); + } else { + ensureNotStrict(context, lastFieldName); } } private static void parseDynamicValue(final DocumentParserContext context, String currentFieldName) throws IOException { - if (context.dynamic() == ObjectMapper.Dynamic.STRICT) { - throw new StrictDynamicMappingException(context.parser().getTokenLocation(), context.parent().fullPath(), currentFieldName); - } + ensureNotStrict(context, currentFieldName); if (context.dynamic() == ObjectMapper.Dynamic.FALSE) { failIfMatchesRoutingPath(context, currentFieldName); return; @@ -687,6 +680,12 @@ private static void parseDynamicValue(final DocumentParserContext context, Strin context.dynamic().getDynamicFieldsBuilder().createDynamicFieldFromValue(context, currentFieldName); } + private static void ensureNotStrict(DocumentParserContext context, String currentFieldName) { + if (context.dynamic() == ObjectMapper.Dynamic.STRICT) { + throw new StrictDynamicMappingException(context.parser().getTokenLocation(), context.parent().fullPath(), currentFieldName); + } + } + private static void failIfMatchesRoutingPath(DocumentParserContext context, String currentFieldName) { if (context.indexSettings().getIndexMetadata().getRoutingPaths().isEmpty()) { return; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java index c3ea8b1c8d65b..3ff77e2192634 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java @@ -15,7 +15,6 @@ import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; -import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -76,14 +75,7 @@ public Mapper.Builder parse(String name, Map node, MappingParser } NestedObjectMapper.Builder builder = new NestedObjectMapper.Builder(name, parserContext.indexVersionCreated()); parseNested(name, node, builder); - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (parseObjectOrDocumentTypeProperties(fieldName, fieldNode, parserContext, builder)) { - iterator.remove(); - } - } + parseObjectFields(node, parserContext, builder); return builder; } @@ -152,18 +144,10 @@ public boolean isIncludeInParent() { return this.includeInParent.value(); } - public void setIncludeInParent(boolean includeInParent) { - this.includeInParent = Explicit.explicitBoolean(includeInParent); - } - public boolean isIncludeInRoot() { return this.includeInRoot.value(); } - public void setIncludeInRoot(boolean includeInRoot) { - this.includeInRoot = Explicit.explicitBoolean(includeInRoot); - } - public Map getChildren() { return this.mappers; } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java index 851892c3a05e9..6bc82cff20e58 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java @@ -61,7 +61,7 @@ DynamicFieldsBuilder getDynamicFieldsBuilder() { DynamicFieldsBuilder getDynamicFieldsBuilder() { throw new UnsupportedOperationException("Cannot create dynamic fields when dynamic is set to [" + this + "]"); - }; + } /** * Get the root-level dynamic setting for a Mapping @@ -198,6 +198,12 @@ public Mapper.Builder parse(String name, Map node, MappingParser parserContext.incrementMappingObjectDepth(); // throws MapperParsingException if depth limit is exceeded Explicit subobjects = parseSubobjects(node); ObjectMapper.Builder builder = new Builder(name, subobjects); + parseObjectFields(node, parserContext, builder); + parserContext.decrementMappingObjectDepth(); + return builder; + } + + static void parseObjectFields(Map node, MappingParserContext parserContext, Builder builder) { for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = iterator.next(); String fieldName = entry.getKey(); @@ -206,8 +212,6 @@ public Mapper.Builder parse(String name, Map node, MappingParser iterator.remove(); } } - parserContext.decrementMappingObjectDepth(); - return builder; } @SuppressWarnings({ "unchecked", "rawtypes" }) From e70d774cad38a2cd8acdc8a830dd0aa81f294d15 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 22 Sep 2023 11:33:11 +0100 Subject: [PATCH 029/155] Extract RecoveryClusterStateDelayListeners (#99768) This utility class is needed in another test in serverless, so this commit extracts it to the test framework. --- .../indices/recovery/IndexRecoveryIT.java | 66 ++------------- .../RecoveryClusterStateDelayListeners.java | 80 +++++++++++++++++++ 2 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index 7ba3b5814eb4b..8ba0f1364bac8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -39,7 +39,6 @@ import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.action.support.ChannelActionListener; import org.elasticsearch.action.support.PlainActionFuture; -import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; import org.elasticsearch.action.support.replication.ReplicationResponse; import org.elasticsearch.cluster.ClusterState; @@ -71,10 +70,6 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.CollectionUtils; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.core.AbstractRefCounted; -import org.elasticsearch.core.RefCounted; -import org.elasticsearch.core.Releasable; import org.elasticsearch.gateway.ReplicaShardAllocatorIT; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; @@ -1643,54 +1638,7 @@ public void testWaitForClusterStateToBeAppliedOnSourceNode() throws Exception { final long initialClusterStateVersion = clusterService().state().version(); - // Helper class to encapsulate the sync mechanism that delays applying cluster states on the primary node until the replica gives - // the go-ahead. - class ClusterStateSyncListeners implements Releasable { - private final Map> clusterStateBarriers = ConcurrentCollections.newConcurrentMap(); - private final SubscribableListener startRecoveryListener = new SubscribableListener<>(); - - private final CountDownLatch completeLatch = new CountDownLatch(1); - private final RefCounted refCounted = AbstractRefCounted.of(completeLatch::countDown); - private final List cleanup = new ArrayList<>(2); - - @Override - public void close() { - refCounted.decRef(); - safeAwait(completeLatch); - cleanup.forEach(Runnable::run); - clusterStateBarriers.values().forEach(l -> l.onResponse(null)); - } - - void addCleanup(Runnable runnable) { - cleanup.add(runnable); - } - - SubscribableListener getStateApplyDelayListener(long clusterStateVersion) { - assertThat(clusterStateVersion, greaterThanOrEqualTo(initialClusterStateVersion)); - if (refCounted.tryIncRef()) { - try { - return clusterStateBarriers.computeIfAbsent(clusterStateVersion, ignored -> new SubscribableListener<>()); - } finally { - refCounted.decRef(); - } - } else { - return SubscribableListener.newSucceeded(null); - } - } - - void onStartRecovery() { - Thread.yield(); - assertFalse(startRecoveryListener.isDone()); - startRecoveryListener.onResponse(null); - } - - public void delayUntilRecoveryStart(SubscribableListener listener) { - assertFalse(startRecoveryListener.isDone()); - startRecoveryListener.addListener(listener); - } - } - - try (var clusterStateSyncListeners = new ClusterStateSyncListeners()) { + try (var recoveryClusterStateDelayListeners = new RecoveryClusterStateDelayListeners(initialClusterStateVersion)) { final var primaryNodeTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, primaryNode @@ -1699,7 +1647,7 @@ public void delayUntilRecoveryStart(SubscribableListener listener) { Coordinator.COMMIT_STATE_ACTION_NAME, (handler, request, channel, task) -> { assertThat(request, instanceOf(ApplyCommitRequest.class)); - clusterStateSyncListeners.getStateApplyDelayListener(((ApplyCommitRequest) request).getVersion()) + recoveryClusterStateDelayListeners.getClusterStateDelayListener(((ApplyCommitRequest) request).getVersion()) .addListener( ActionListener.wrap(ignored -> handler.messageReceived(request, channel, task), e -> fail(e, "unexpected")) ); @@ -1719,28 +1667,28 @@ public void delayUntilRecoveryStart(SubscribableListener listener) { ), task ); - clusterStateSyncListeners.onStartRecovery(); + recoveryClusterStateDelayListeners.onStartRecovery(); } ); - clusterStateSyncListeners.addCleanup(primaryNodeTransportService::clearInboundRules); + recoveryClusterStateDelayListeners.addCleanup(primaryNodeTransportService::clearInboundRules); final var replicaClusterService = internalCluster().getInstance(ClusterService.class, replicaNode); final ClusterStateListener clusterStateListener = event -> { - final var primaryProceedListener = clusterStateSyncListeners.getStateApplyDelayListener(event.state().version()); + final var primaryProceedListener = recoveryClusterStateDelayListeners.getClusterStateDelayListener(event.state().version()); final var indexRoutingTable = event.state().routingTable().index(indexName); assertNotNull(indexRoutingTable); final var indexShardRoutingTable = indexRoutingTable.shard(0); if (indexShardRoutingTable.size() == 2 && indexShardRoutingTable.getAllInitializingShards().isEmpty() == false) { // this is the cluster state update which starts the recovery, so delay the primary node application until recovery // has started - clusterStateSyncListeners.delayUntilRecoveryStart(primaryProceedListener); + recoveryClusterStateDelayListeners.delayUntilRecoveryStart(primaryProceedListener); } else { // this is some other cluster state update, so we must let it proceed now primaryProceedListener.onResponse(null); } }; replicaClusterService.addListener(clusterStateListener); - clusterStateSyncListeners.addCleanup(() -> replicaClusterService.removeListener(clusterStateListener)); + recoveryClusterStateDelayListeners.addCleanup(() -> replicaClusterService.removeListener(clusterStateListener)); updateIndexSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1), indexName); ensureGreen(indexName); diff --git a/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java b/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java new file mode 100644 index 0000000000000..ba73845c7f63a --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.indices.recovery; + +import org.elasticsearch.action.support.SubscribableListener; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.core.AbstractRefCounted; +import org.elasticsearch.core.RefCounted; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.test.ESTestCase; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.hamcrest.Matchers.greaterThanOrEqualTo; + +/** + * Allows one node in an integ test to delay the application of cluster states on another node until a recovery starts, without unduly + * delaying other cluster states. The controlling node gets a listener for each cluster state version it applies using {@link + * #getClusterStateDelayListener} and either completes it straight away (when applying states unrelated to the recovery) or delays it with + * {@link #delayUntilRecoveryStart} and releases the delay with {@link #onStartRecovery()}; meanwhile the other node gets the same listener + * for each cluster state commit message and uses it to delay handling the commit. + */ +public class RecoveryClusterStateDelayListeners implements Releasable { + private final Map> clusterStateBarriers = ConcurrentCollections.newConcurrentMap(); + private final SubscribableListener startRecoveryListener = new SubscribableListener<>(); + + private final CountDownLatch completeLatch = new CountDownLatch(1); + private final RefCounted refCounted = AbstractRefCounted.of(completeLatch::countDown); + private final List cleanup = new ArrayList<>(2); + private final long initialClusterStateVersion; + + public RecoveryClusterStateDelayListeners(long initialClusterStateVersion) { + this.initialClusterStateVersion = initialClusterStateVersion; + } + + @Override + public void close() { + refCounted.decRef(); + ESTestCase.safeAwait(completeLatch); + cleanup.forEach(Runnable::run); + clusterStateBarriers.values().forEach(l -> l.onResponse(null)); + } + + void addCleanup(Runnable runnable) { + cleanup.add(runnable); + } + + SubscribableListener getClusterStateDelayListener(long clusterStateVersion) { + ESTestCase.assertThat(clusterStateVersion, greaterThanOrEqualTo(initialClusterStateVersion)); + if (refCounted.tryIncRef()) { + try { + return clusterStateBarriers.computeIfAbsent(clusterStateVersion, ignored -> new SubscribableListener<>()); + } finally { + refCounted.decRef(); + } + } else { + return SubscribableListener.newSucceeded(null); + } + } + + void onStartRecovery() { + Thread.yield(); + ESTestCase.assertFalse(startRecoveryListener.isDone()); + startRecoveryListener.onResponse(null); + } + + public void delayUntilRecoveryStart(SubscribableListener listener) { + ESTestCase.assertFalse(startRecoveryListener.isDone()); + startRecoveryListener.addListener(listener); + } +} From 3d607c6dac88c3c03a665dad4661004be8a497ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Fri, 22 Sep 2023 13:00:17 +0200 Subject: [PATCH 030/155] [Transform] Do not use PIT in the presence of remote indices in source (#99803) --- docs/changelog/99803.yaml | 5 ++ .../transforms/ClientTransformIndexer.java | 5 +- .../ClientTransformIndexerTests.java | 61 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/99803.yaml diff --git a/docs/changelog/99803.yaml b/docs/changelog/99803.yaml new file mode 100644 index 0000000000000..ce0929eb20e07 --- /dev/null +++ b/docs/changelog/99803.yaml @@ -0,0 +1,5 @@ +pr: 99803 +summary: Do not use PIT in the presence of remote indices in source +area: Transform +type: bug +issues: [] diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java index b3373eac02968..00fa7f200a3c3 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java @@ -146,7 +146,7 @@ protected void doNextSearch(long waitTimeInNanos, ActionListener injectPointInTimeIfNeeded( buildSearchRequest(), - ActionListener.wrap(pitSearchRequest -> doSearch(pitSearchRequest, nextPhase), nextPhase::onFailure) + ActionListener.wrap(searchRequest -> doSearch(searchRequest, nextPhase), nextPhase::onFailure) ); } @@ -435,7 +435,8 @@ private void injectPointInTimeIfNeeded( ActionListener> listener ) { SearchRequest searchRequest = namedSearchRequest.v2(); - if (disablePit || searchRequest.indices().length == 0) { + // We explicitly disable PIT in the presence of remote clusters in the source due to huge PIT handles causing performance problems. + if (disablePit || searchRequest.indices().length == 0 || transformConfig.getSource().requiresRemoteCluster()) { listener.onResponse(namedSearchRequest); return; } diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java index ad2976e9a3dc1..09b46c72c2ead 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java @@ -40,6 +40,7 @@ import org.elasticsearch.transport.ActionNotFoundTransportException; import org.elasticsearch.xpack.core.indexing.IndexerState; import org.elasticsearch.xpack.core.transform.transforms.SettingsConfig; +import org.elasticsearch.xpack.core.transform.transforms.SourceConfig; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpoint; import org.elasticsearch.xpack.core.transform.transforms.TransformConfig; import org.elasticsearch.xpack.core.transform.transforms.TransformConfigTests; @@ -290,6 +291,7 @@ public void testPitInjectionIfPitNotSupported() throws InterruptedException { } public void testDisablePit() throws InterruptedException { + // TransformConfigTests.randomTransformConfig never produces remote indices in the source, hence we are safe here. */ TransformConfig config = TransformConfigTests.randomTransformConfig(); boolean pitEnabled = config.getSettings().getUsePit() == null || config.getSettings().getUsePit(); @@ -349,6 +351,65 @@ public void testDisablePit() throws InterruptedException { } } + public void testDisablePitWhenThereIsRemoteIndexInSource() throws InterruptedException { + TransformConfig config = new TransformConfig.Builder(TransformConfigTests.randomTransformConfig()) + // Remote index is configured within source + .setSource(new SourceConfig("remote-cluster:remote-index")) + .build(); + boolean pitEnabled = config.getSettings().getUsePit() == null || config.getSettings().getUsePit(); + + try (PitMockClient client = new PitMockClient(getTestName(), true)) { + MockClientTransformIndexer indexer = new MockClientTransformIndexer( + mock(ThreadPool.class), + new TransformServices( + mock(IndexBasedTransformConfigManager.class), + mock(TransformCheckpointService.class), + mock(TransformAuditor.class), + new TransformScheduler(Clock.systemUTC(), mock(ThreadPool.class), Settings.EMPTY) + ), + mock(CheckpointProvider.class), + new AtomicReference<>(IndexerState.STOPPED), + null, + client, + mock(TransformIndexerStats.class), + config, + null, + new TransformCheckpoint( + "transform", + Instant.now().toEpochMilli(), + 0L, + Collections.emptyMap(), + Instant.now().toEpochMilli() + ), + new TransformCheckpoint( + "transform", + Instant.now().toEpochMilli(), + 2L, + Collections.emptyMap(), + Instant.now().toEpochMilli() + ), + new SeqNoPrimaryTermAndIndex(1, 1, TransformInternalIndexConstants.LATEST_INDEX_NAME), + mock(TransformContext.class), + false + ); + + // Because remote index is configured within source, we expect PIT *not* being used regardless the transform settings + this.assertAsync( + listener -> indexer.doNextSearch(0, listener), + response -> assertNull(response.pointInTimeId()) + ); + + // reverse the setting + indexer.applyNewSettings(new SettingsConfig.Builder().setUsePit(pitEnabled == false).build()); + + // Because remote index is configured within source, we expect PIT *not* being used regardless the transform settings + this.assertAsync( + listener -> indexer.doNextSearch(0, listener), + response -> assertNull(response.pointInTimeId()) + ); + } + } + public void testHandlePitIndexNotFound() throws InterruptedException { // simulate a deleted index due to ILM try (PitMockClient client = new PitMockClient(getTestName(), true)) { From 343cc8047cce51a82c018c3d382463be75ef0e0b Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Fri, 22 Sep 2023 12:26:16 +0100 Subject: [PATCH 031/155] Correctly handle ScriptScoreQuery in plain highlighter (#99804) ScriptScoreQuery is an elasticsearch query implementation that the plain highlighter doesn't know anything about. To highlight it correctly, we need to add some custom handling logic in our CustomQueryScorer. Fixes #99700 --- docs/changelog/99804.yaml | 6 +++ .../search/function/ScriptScoreQuery.java | 4 ++ .../index/query/QueryRewriteContext.java | 6 +-- .../index/query/SearchExecutionContext.java | 6 +-- .../subphase/highlight/CustomQueryScorer.java | 3 ++ .../highlight/PlainHighlighterTests.java | 51 +++++++++++++++++++ .../index/mapper/MapperServiceTestCase.java | 2 +- 7 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 docs/changelog/99804.yaml diff --git a/docs/changelog/99804.yaml b/docs/changelog/99804.yaml new file mode 100644 index 0000000000000..b4c226217e352 --- /dev/null +++ b/docs/changelog/99804.yaml @@ -0,0 +1,6 @@ +pr: 99804 +summary: Correctly handle `ScriptScoreQuery` in plain highlighter +area: Highlighting +type: bug +issues: + - 99700 diff --git a/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreQuery.java b/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreQuery.java index 14ca16affe7e7..7ddaa4bc681fa 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreQuery.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreQuery.java @@ -67,6 +67,10 @@ public ScriptScoreQuery( this.indexVersion = indexVersion; } + public Query getSubQuery() { + return subQuery; + } + @Override public Query rewrite(IndexSearcher searcher) throws IOException { Query newQ = subQuery.rewrite(searcher); diff --git a/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 109e1394a3066..117203d4a9253 100644 --- a/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -20,7 +20,7 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MappingLookup; import org.elasticsearch.index.mapper.TextFieldMapper; -import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.ScriptCompiler; import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParserConfiguration; @@ -50,7 +50,7 @@ public class QueryRewriteContext { protected final NamedWriteableRegistry writeableRegistry; protected final ValuesSourceRegistry valuesSourceRegistry; protected final BooleanSupplier allowExpensiveQueries; - protected final ScriptService scriptService; + protected final ScriptCompiler scriptService; private final XContentParserConfiguration parserConfiguration; protected final Client client; protected final LongSupplier nowInMillis; @@ -73,7 +73,7 @@ public QueryRewriteContext( final NamedWriteableRegistry namedWriteableRegistry, final ValuesSourceRegistry valuesSourceRegistry, final BooleanSupplier allowExpensiveQueries, - final ScriptService scriptService + final ScriptCompiler scriptService ) { this.parserConfiguration = parserConfiguration; diff --git a/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java b/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java index 6f0c589d3f25b..746ada6fe4dcf 100644 --- a/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java +++ b/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java @@ -48,9 +48,9 @@ import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptCompiler; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptFactory; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.NestedDocuments; import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry; import org.elasticsearch.search.lookup.LeafFieldLookupProvider; @@ -110,7 +110,7 @@ public SearchExecutionContext( MapperService mapperService, MappingLookup mappingLookup, SimilarityService similarityService, - ScriptService scriptService, + ScriptCompiler scriptService, XContentParserConfiguration parserConfiguration, NamedWriteableRegistry namedWriteableRegistry, Client client, @@ -183,7 +183,7 @@ private SearchExecutionContext( MapperService mapperService, MappingLookup mappingLookup, SimilarityService similarityService, - ScriptService scriptService, + ScriptCompiler scriptService, XContentParserConfiguration parserConfig, NamedWriteableRegistry namedWriteableRegistry, Client client, diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java index 3779c35f910ac..31e704fe30ff9 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java @@ -14,6 +14,7 @@ import org.apache.lucene.search.highlight.WeightedSpanTerm; import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; +import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery; import org.elasticsearch.index.search.ESToParentBlockJoinQuery; import java.io.IOException; @@ -73,6 +74,8 @@ protected void extract(Query query, float boost, Map t super.extract(((FunctionScoreQuery) query).getSubQuery(), boost, terms); } else if (query instanceof ESToParentBlockJoinQuery) { super.extract(((ESToParentBlockJoinQuery) query).getChildQuery(), boost, terms); + } else if (query instanceof ScriptScoreQuery ssq) { + super.extract(ssq.getSubQuery(), boost, terms); } else { super.extract(query, boost, terms); } diff --git a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighterTests.java b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighterTests.java index eb570433e0ef2..a6c95efef9c46 100644 --- a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighterTests.java +++ b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighterTests.java @@ -16,9 +16,14 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.script.DocReader; +import org.elasticsearch.script.ScoreScript; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptContext; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.HighlighterTestCase; +import java.io.IOException; import java.util.Map; public class PlainHighlighterTests extends HighlighterTestCase { @@ -96,4 +101,50 @@ public void testOrdering() throws Exception { } } + + public void testScriptScoreQuery() throws IOException { + MapperService mapperService = createMapperService(""" + { "_doc" : { "properties" : { + "text" : { "type" : "text" } + }}} + """); + + ParsedDocument doc = mapperService.documentMapper().parse(source(""" + { "text" : "some text" } + """)); + + SearchSourceBuilder search = new SearchSourceBuilder().query( + QueryBuilders.scriptScoreQuery( + QueryBuilders.boolQuery().should(QueryBuilders.termQuery("text", "some")).must(QueryBuilders.existsQuery("text")), + new Script("foo") + ) + ).highlighter(new HighlightBuilder().field("text").highlighterType("plain")); + + assertHighlights(highlight(mapperService, doc, search), "text", "some text"); + } + + @Override + @SuppressWarnings("unchecked") + protected T compileScript(Script script, ScriptContext context) { + if (context == ScoreScript.CONTEXT) { + // Dummy script for testing ScriptScoreQuery highlighting + return (T) (ScoreScript.Factory) (params, lookup) -> new ScoreScript.LeafFactory() { + @Override + public boolean needs_score() { + return true; + } + + @Override + public ScoreScript newInstance(DocReader reader) throws IOException { + return new ScoreScript(params, lookup, reader) { + @Override + public double execute(ExplanationHolder explanation) { + return 0; + } + }; + } + }; + } + return super.compileScript(script, context); + } } diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java index 371ea3f35292b..11cfe0150b07a 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java @@ -652,7 +652,7 @@ public void onRemoval(ShardId shardId, Accountable accountable) { mapperService, mapperService.mappingLookup(), similarityService, - null, + MapperServiceTestCase.this::compileScript, parserConfig(), writableRegistry(), null, From 0efa67821d5c7becb1f1c1115d26c58fe6734301 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Fri, 22 Sep 2023 13:35:36 +0200 Subject: [PATCH 032/155] Rename TracerPlugin to TelemetryPlugin (#99735) with the support of metrics the TracerPlugin name is no longer adequate. Renaming this to TelemetryPlugin. Also introducing TelemetryProvider interface. While it is only used in Node.java at the moment to fetch Tracer instance, it is intended to be used in Plugin::createComponents (to be done in separate commit due to the broad scope of this method) This will allow for plugins to get access to both Tracer and Metric interfaces without the need to add yet another argument to createComponents Also adding internal subpackage in module/apm so that it is more obvious which packages are not exported --- TRACING.md | 2 +- .../server/cli/APMJvmOptions.java | 4 +-- .../org/elasticsearch/telemetry/apm/APM.java | 24 ++++++++-------- .../APMAgentSettings.java | 8 ++++-- .../apm/internal/APMTelemetryProvider.java | 28 +++++++++++++++++++ .../apm/{ => internal}/tracing/APMTracer.java | 10 +++---- .../APMAgentSettingsTests.java | 2 +- .../tracing/APMTracerTests.java | 4 +-- server/src/main/java/module-info.java | 1 + .../java/org/elasticsearch/node/Node.java | 16 ++++++----- ...TracerPlugin.java => TelemetryPlugin.java} | 6 ++-- .../telemetry/TelemetryProvider.java | 24 ++++++++++++++++ 12 files changed, 94 insertions(+), 35 deletions(-) rename modules/apm/src/main/java/org/elasticsearch/telemetry/apm/{settings => internal}/APMAgentSettings.java (96%) create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java rename modules/apm/src/main/java/org/elasticsearch/telemetry/apm/{ => internal}/tracing/APMTracer.java (98%) rename modules/apm/src/test/java/org/elasticsearch/telemetry/apm/{settings => internal}/APMAgentSettingsTests.java (98%) rename modules/apm/src/test/java/org/elasticsearch/telemetry/apm/{ => internal}/tracing/APMTracerTests.java (99%) rename server/src/main/java/org/elasticsearch/plugins/{TracerPlugin.java => TelemetryPlugin.java} (74%) create mode 100644 server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java diff --git a/TRACING.md b/TRACING.md index 3935a94eff160..6221305ef455b 100644 --- a/TRACING.md +++ b/TRACING.md @@ -158,6 +158,6 @@ explicitly opening a scope via the `Tracer`. [otel]: https://opentelemetry.io/ [thread-context]: ./server/src/main/java/org/elasticsearch/common/util/concurrent/ThreadContext.java [w3c]: https://www.w3.org/TR/trace-context/ -[tracing]: ./server/src/main/java/org/elasticsearch/tracing +[tracing]: ./server/src/main/java/org/elasticsearch/telemetry [agent-config]: https://www.elastic.co/guide/en/apm/agent/java/master/configuration.html [agent]: https://www.elastic.co/guide/en/apm/agent/java/current/index.html diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java index 9dcfc1e373a93..6d832dfff2758 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java @@ -32,7 +32,7 @@ import java.util.StringJoiner; /** - * This class is responsible for working out if APM tracing is configured and if so, preparing + * This class is responsible for working out if APM telemetry is configured and if so, preparing * a temporary config file for the APM Java agent and CLI options to the JVM to configure APM. * APM doesn't need to be enabled, as that can be toggled at runtime, but some configuration e.g. * server URL and secret key can only be provided when Elasticsearch starts. @@ -128,7 +128,7 @@ class APMJvmOptions { ); /** - * This method works out if APM tracing is enabled, and if so, prepares a temporary config file + * This method works out if APM telemetry is enabled, and if so, prepares a temporary config file * for the APM Java agent and CLI options to the JVM to configure APM. The config file is temporary * because it will be deleted once Elasticsearch starts. * diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java index 1208fa1f7b9e5..9f1285c05730b 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java @@ -21,11 +21,13 @@ import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.NetworkPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.plugins.TracerPlugin; +import org.elasticsearch.plugins.TelemetryPlugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.apm.settings.APMAgentSettings; -import org.elasticsearch.telemetry.apm.tracing.APMTracer; +import org.elasticsearch.telemetry.TelemetryProvider; +import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; +import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider; +import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -55,8 +57,8 @@ * be passed via system properties to the Java agent, which periodically checks for changes * and applies the new settings values, provided those settings can be dynamically updated. */ -public class APM extends Plugin implements NetworkPlugin, TracerPlugin { - private final SetOnce tracer = new SetOnce<>(); +public class APM extends Plugin implements NetworkPlugin, TelemetryPlugin { + private final SetOnce telemetryProvider = new SetOnce<>(); private final Settings settings; public APM(Settings settings) { @@ -64,10 +66,10 @@ public APM(Settings settings) { } @Override - public Tracer getTracer(Settings settings) { - final APMTracer apmTracer = new APMTracer(settings); - tracer.set(apmTracer); - return apmTracer; + public TelemetryProvider getTelemetryProvider(Settings settings) { + final APMTelemetryProvider apmTelemetryProvider = new APMTelemetryProvider(settings); + telemetryProvider.set(apmTelemetryProvider); + return apmTelemetryProvider; } @Override @@ -87,14 +89,14 @@ public Collection createComponents( AllocationService allocationService, IndicesService indicesService ) { - final APMTracer apmTracer = tracer.get(); + final APMTracer apmTracer = telemetryProvider.get().getTracer(); apmTracer.setClusterName(clusterService.getClusterName().value()); apmTracer.setNodeName(clusterService.getNodeName()); final APMAgentSettings apmAgentSettings = new APMAgentSettings(); apmAgentSettings.syncAgentSystemProperties(settings); - apmAgentSettings.addClusterSettingsListeners(clusterService, apmTracer); + apmAgentSettings.addClusterSettingsListeners(clusterService, telemetryProvider.get()); return List.of(apmTracer); } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettings.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java similarity index 96% rename from modules/apm/src/main/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettings.java rename to modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java index 49317ab36543d..75ca94bb13ad6 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettings.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.telemetry.apm.settings; +package org.elasticsearch.telemetry.apm.internal; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -17,7 +17,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.telemetry.apm.tracing.APMTracer; +import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; import java.security.AccessController; import java.security.PrivilegedAction; @@ -42,8 +42,10 @@ public class APMAgentSettings { */ static Map APM_AGENT_DEFAULT_SETTINGS = Map.of("transaction_sample_rate", "0.2"); - public void addClusterSettingsListeners(ClusterService clusterService, APMTracer apmTracer) { + public void addClusterSettingsListeners(ClusterService clusterService, APMTelemetryProvider apmTelemetryProvider) { final ClusterSettings clusterSettings = clusterService.getClusterSettings(); + final APMTracer apmTracer = apmTelemetryProvider.getTracer(); + clusterSettings.addSettingsUpdateConsumer(APM_ENABLED_SETTING, enabled -> { apmTracer.setEnabled(enabled); // The agent records data other than spans, e.g. JVM metrics, so we toggle this setting in order to diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java new file mode 100644 index 0000000000000..495afd43bf176 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.telemetry.TelemetryProvider; +import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; + +public class APMTelemetryProvider implements TelemetryProvider { + private final Settings settings; + private final APMTracer apmTracer; + + public APMTelemetryProvider(Settings settings) { + this.settings = settings; + apmTracer = new APMTracer(settings); + } + + @Override + public APMTracer getTracer() { + return apmTracer; + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/tracing/APMTracer.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java similarity index 98% rename from modules/apm/src/main/java/org/elasticsearch/telemetry/apm/tracing/APMTracer.java rename to modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java index fc390f1c3d603..daedb90047975 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/tracing/APMTracer.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.telemetry.apm.tracing; +package org.elasticsearch.telemetry.apm.internal.tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; @@ -43,10 +43,10 @@ import java.util.Map; import java.util.stream.Collectors; -import static org.elasticsearch.telemetry.apm.settings.APMAgentSettings.APM_ENABLED_SETTING; -import static org.elasticsearch.telemetry.apm.settings.APMAgentSettings.APM_TRACING_NAMES_EXCLUDE_SETTING; -import static org.elasticsearch.telemetry.apm.settings.APMAgentSettings.APM_TRACING_NAMES_INCLUDE_SETTING; -import static org.elasticsearch.telemetry.apm.settings.APMAgentSettings.APM_TRACING_SANITIZE_FIELD_NAMES; +import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.APM_ENABLED_SETTING; +import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.APM_TRACING_NAMES_EXCLUDE_SETTING; +import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.APM_TRACING_NAMES_INCLUDE_SETTING; +import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.APM_TRACING_SANITIZE_FIELD_NAMES; /** * This is an implementation of the {@link org.elasticsearch.telemetry.tracing.Tracer} interface, which uses diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettingsTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettingsTests.java similarity index 98% rename from modules/apm/src/test/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettingsTests.java rename to modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettingsTests.java index 3d95c9f85f5e5..b22a57bb9bf0c 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/settings/APMAgentSettingsTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettingsTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.telemetry.apm.settings; +package org.elasticsearch.telemetry.apm.internal; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/tracing/APMTracerTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracerTests.java similarity index 99% rename from modules/apm/src/test/java/org/elasticsearch/telemetry/apm/tracing/APMTracerTests.java rename to modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracerTests.java index ffe719197c59f..8cb94b782756d 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/tracing/APMTracerTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracerTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.telemetry.apm.tracing; +package org.elasticsearch.telemetry.apm.internal.tracing; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -21,7 +21,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.apm.settings.APMAgentSettings; +import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; import org.elasticsearch.telemetry.tracing.SpanId; import org.elasticsearch.test.ESTestCase; diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 19fe187eaa080..1568f47461e6c 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -383,6 +383,7 @@ org.elasticsearch.serverless.constants, org.elasticsearch.serverless.apifiltering; exports org.elasticsearch.telemetry.tracing; + exports org.elasticsearch.telemetry; provides java.util.spi.CalendarDataProvider with org.elasticsearch.common.time.IsoCalendarDataProvider; provides org.elasticsearch.xcontent.ErrorOnUnknown with org.elasticsearch.common.xcontent.SuggestingErrorOnUnknown; diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 198abe36e1452..4e1d6ece3602b 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -179,7 +179,7 @@ import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.plugins.ShutdownAwarePlugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.plugins.TracerPlugin; +import org.elasticsearch.plugins.TelemetryPlugin; import org.elasticsearch.plugins.internal.DocumentParsingObserver; import org.elasticsearch.plugins.internal.DocumentParsingObserverPlugin; import org.elasticsearch.plugins.internal.ReloadAwarePlugin; @@ -213,6 +213,7 @@ import org.elasticsearch.tasks.TaskCancellationService; import org.elasticsearch.tasks.TaskManager; import org.elasticsearch.tasks.TaskResultsService; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -456,7 +457,8 @@ protected Node( Task.HEADERS_TO_COPY.stream() ).collect(Collectors.toSet()); - final Tracer tracer = getTracer(pluginsService, settings); + final TelemetryProvider telemetryProvider = getTelemetryProvider(pluginsService, settings); + final Tracer tracer = telemetryProvider.getTracer(); final TaskManager taskManager = new TaskManager(settings, threadPool, taskHeaders, tracer); @@ -1287,14 +1289,14 @@ private static ReloadablePlugin wrapPlugins(List reloadablePlu }; } - private Tracer getTracer(PluginsService pluginsService, Settings settings) { - final List tracerPlugins = pluginsService.filterPlugins(TracerPlugin.class); + private TelemetryProvider getTelemetryProvider(PluginsService pluginsService, Settings settings) { + final List telemetryPlugins = pluginsService.filterPlugins(TelemetryPlugin.class); - if (tracerPlugins.size() > 1) { - throw new IllegalStateException("A single TracerPlugin was expected but got: " + tracerPlugins); + if (telemetryPlugins.size() > 1) { + throw new IllegalStateException("A single TelemetryPlugin was expected but got: " + telemetryPlugins); } - return tracerPlugins.isEmpty() ? Tracer.NOOP : tracerPlugins.get(0).getTracer(settings); + return telemetryPlugins.isEmpty() ? TelemetryProvider.NOOP : telemetryPlugins.get(0).getTelemetryProvider(settings); } private HealthService createHealthService( diff --git a/server/src/main/java/org/elasticsearch/plugins/TracerPlugin.java b/server/src/main/java/org/elasticsearch/plugins/TelemetryPlugin.java similarity index 74% rename from server/src/main/java/org/elasticsearch/plugins/TracerPlugin.java rename to server/src/main/java/org/elasticsearch/plugins/TelemetryPlugin.java index eccc38cf9f5c9..4f47da99a3d5f 100644 --- a/server/src/main/java/org/elasticsearch/plugins/TracerPlugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/TelemetryPlugin.java @@ -9,8 +9,8 @@ package org.elasticsearch.plugins; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; -public interface TracerPlugin { - Tracer getTracer(Settings settings); +public interface TelemetryPlugin { + TelemetryProvider getTelemetryProvider(Settings settings); } diff --git a/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java new file mode 100644 index 0000000000000..0df8aeedac7f8 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.telemetry.tracing.Tracer; + +public interface TelemetryProvider { + Tracer getTracer(); + + TelemetryProvider NOOP = new TelemetryProvider() { + + @Override + public Tracer getTracer() { + return Tracer.NOOP; + } + + }; +} From 05879651ff23786d9dd34bcd059f075bd062e000 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Fri, 22 Sep 2023 14:11:23 +0200 Subject: [PATCH 033/155] Fix expectations of IndexVersionTests#testLuceneVersionOnUnknownVersions. (#99799) This test tests the following: - an unknown version has the Lucene version of the previous Elasticsearch version - an unknown version older than the first known version is on the previous Lucene major version - an unknown version in the future is on the same Lucene version as the current Lucene version Co-authored-by: Simon Cooper --- .../index/IndexVersionTests.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java index 452da5279f4c1..bfb7298d5bfca 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java @@ -199,23 +199,20 @@ public void testParseLenient() { public void testLuceneVersionOnUnknownVersions() { // between two known versions, should use the lucene version of the previous version - IndexVersion version = IndexVersionUtils.getPreviousVersion(); - final IndexVersion nextVersion = IndexVersion.fromId(version.id() + 100); - if (IndexVersionUtils.allReleasedVersions().contains(nextVersion) == false) { - // the version is not known, we make an assumption the Lucene version stays the same - assertThat(version.luceneVersion(), equalTo(nextVersion.luceneVersion())); - } else { - // the version is known, the most we can assert is that the Lucene version is not earlier - // Version does not implement Comparable :( - assertTrue(nextVersion.luceneVersion().onOrAfter(version.luceneVersion())); - } + IndexVersion previousVersion = IndexVersionUtils.getPreviousVersion(); + IndexVersion currentVersion = IndexVersion.current(); + int intermediateVersionId = previousVersion.id() + randomInt(currentVersion.id() - previousVersion.id() - 1); + IndexVersion intermediateVersion = IndexVersion.fromId(intermediateVersionId); + // the version is either the previous version or between the previous version and the current version excluded, so it is assumed to + // use the same Lucene version as the previous version + assertThat(intermediateVersion.luceneVersion(), equalTo(previousVersion.luceneVersion())); // too old version, major should be the oldest supported lucene version minus 1 - version = IndexVersion.fromId(5020199); - assertThat(version.luceneVersion().major, equalTo(IndexVersionUtils.getFirstVersion().luceneVersion().major - 1)); + IndexVersion oldVersion = IndexVersion.fromId(5020199); + assertThat(oldVersion.luceneVersion().major, equalTo(IndexVersionUtils.getFirstVersion().luceneVersion().major - 1)); // future version, should be the same version as today - version = IndexVersion.fromId(IndexVersion.current().id() + 100); - assertThat(version.luceneVersion(), equalTo(IndexVersion.current().luceneVersion())); + IndexVersion futureVersion = IndexVersion.fromId(currentVersion.id() + 100); + assertThat(futureVersion.luceneVersion(), equalTo(currentVersion.luceneVersion())); } } From eca41871aa6e05eb2df5a8178d012fdc893250ab Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Fri, 22 Sep 2023 14:48:11 +0200 Subject: [PATCH 034/155] Use TelemetryProvider in Plugin::createComponents (#99737) in order to avoid adding yet anther parameter to createComponents a Tracer interface is replaced with TelemetryProvider. this allows to get both Tracer and Metric (in the future) interfaces --- .../analysis/common/CommonAnalysisPlugin.java | 4 ++-- .../PredicateTokenScriptFilterTests.java | 19 +++++++++++++++++-- .../ScriptedConditionTokenFilterTests.java | 19 +++++++++++++++++-- .../org/elasticsearch/telemetry/apm/APM.java | 3 +-- .../datastreams/DataStreamsPlugin.java | 4 ++-- .../ingest/geoip/IngestGeoIpPlugin.java | 4 ++-- .../painless/PainlessPlugin.java | 4 ++-- .../elasticsearch/reindex/ReindexPlugin.java | 4 ++-- .../ReindexFromRemoteWithAuthTests.java | 4 ++-- .../azure/AzureRepositoryPlugin.java | 4 ++-- .../azure/AzureStorageServiceTests.java | 4 ++-- .../repositories/s3/S3RepositoryPlugin.java | 4 ++-- .../repository/url/URLRepositoryPlugin.java | 4 ++-- .../RuntimeFieldsCommonPlugin.java | 4 ++-- .../elasticsearch/systemd/SystemdPlugin.java | 4 ++-- .../systemd/SystemdPluginTests.java | 12 ++++++------ .../cluster/SimpleClusterStateIT.java | 4 ++-- .../metadata/TemplateUpgradeServiceIT.java | 6 +++--- .../health/GetHealthActionIT.java | 4 ++-- .../elasticsearch/health/HealthServiceIT.java | 4 ++-- .../elasticsearch/index/FinalPipelineIT.java | 4 ++-- .../index/SettingsListenerIT.java | 4 ++-- .../ingest/IngestAsyncProcessorIT.java | 4 ++-- .../java/org/elasticsearch/node/Node.java | 2 +- .../org/elasticsearch/plugins/Plugin.java | 6 +++--- .../org/elasticsearch/node/NodeTests.java | 4 ++-- .../plugins/PluginIntrospectorTests.java | 4 ++-- .../test/seektracker/SeekTrackerPlugin.java | 4 ++-- .../test/MockIndexEventListener.java | 4 ++-- .../xpack/analytics/AnalyticsPlugin.java | 4 ++-- .../xpack/async/AsyncResultsIndexPlugin.java | 4 ++-- .../xpack/autoscaling/Autoscaling.java | 4 ++-- .../java/org/elasticsearch/xpack/ccr/Ccr.java | 4 ++-- .../elasticsearch/xpack/core/XPackPlugin.java | 4 ++-- .../core/LocalStateCompositeXPackPlugin.java | 7 ++++--- .../xpack/deprecation/Deprecation.java | 4 ++-- .../xpack/enrich/EnrichPlugin.java | 4 ++-- .../xpack/application/EnterpriseSearch.java | 4 ++-- .../LocalStateEnterpriseSearch.java | 6 +++--- .../xpack/eql/plugin/EqlPlugin.java | 4 ++-- .../xpack/esql/plugin/EsqlPlugin.java | 4 ++-- .../org/elasticsearch/xpack/fleet/Fleet.java | 4 ++-- .../xpack/idp/IdentityProviderPlugin.java | 4 ++-- .../xpack/ilm/UpdateSettingsStepTests.java | 4 ++-- .../xpack/ilm/IndexLifecycle.java | 4 ++-- .../xpack/inference/InferencePlugin.java | 4 ++-- .../xpack/ml/MachineLearning.java | 4 ++-- .../xpack/monitoring/Monitoring.java | 4 ++-- .../xpack/lucene/bwc/OldLuceneVersions.java | 4 ++-- .../xpack/profiling/ProfilingPlugin.java | 4 ++-- .../SearchableSnapshots.java | 4 ++-- .../xpack/security/Security.java | 3 ++- .../xpack/shutdown/NodeShutdownTasksIT.java | 4 ++-- .../xpack/shutdown/ShutdownPlugin.java | 4 ++-- .../xpack/slm/SnapshotLifecycle.java | 4 ++-- .../xpack/sql/plugin/SqlPlugin.java | 4 ++-- .../xpack/stack/StackPlugin.java | 4 ++-- .../xpack/transform/Transform.java | 4 ++-- .../votingonly/VotingOnlyNodePlugin.java | 4 ++-- .../elasticsearch/xpack/watcher/Watcher.java | 4 ++-- .../xpack/watcher/WatcherPluginTests.java | 4 ++-- 61 files changed, 159 insertions(+), 128 deletions(-) diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index 6baa2ddf6c972..dd2fbe8a19bab 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -131,7 +131,7 @@ import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.synonyms.SynonymsManagementAPIService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -168,7 +168,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java index af7386ba9b629..7b60c1a64abb6 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java @@ -28,7 +28,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTokenStreamTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.threadpool.ThreadPool; @@ -68,7 +68,22 @@ public FactoryType compile(Script script, ScriptContext FactoryType compile(Script script, ScriptContext createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer unused, + TelemetryProvider unused, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java index 3ec77b848d168..de128c685ae98 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java @@ -70,7 +70,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -170,7 +170,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java index fd40643281da2..afc6fa8a1c92a 100644 --- a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java +++ b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java @@ -48,7 +48,7 @@ import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -119,7 +119,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java index 34e5ed3a5e5b7..52eccbe0dce90 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java @@ -43,7 +43,7 @@ import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptModule; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -147,7 +147,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java index 569c510788b7f..e79f9bdaffee9 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java @@ -35,7 +35,7 @@ import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -100,7 +100,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java index 3be475a9483bf..55b7b2cc902cc 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java @@ -45,7 +45,7 @@ import org.elasticsearch.rest.root.MainRestPlugin; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.netty4.Netty4Plugin; @@ -176,7 +176,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java index cc57692f7ba0c..f88a96e765827 100644 --- a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java +++ b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java @@ -30,7 +30,7 @@ import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -96,7 +96,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureStorageServiceTests.java b/modules/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureStorageServiceTests.java index fad293030cbd2..a7a10a17668bc 100644 --- a/modules/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureStorageServiceTests.java +++ b/modules/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureStorageServiceTests.java @@ -14,7 +14,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.common.settings.SettingsModule; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -73,7 +73,7 @@ public void testReadSecuredSettings() { private AzureRepositoryPlugin pluginWithSettingsValidation(Settings settings) { final AzureRepositoryPlugin plugin = new AzureRepositoryPlugin(settings); new SettingsModule(settings, plugin.getSettings(), Collections.emptyList()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); return plugin; } diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java index 120d421fdf48c..62fe279726164 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -31,7 +31,7 @@ import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -102,7 +102,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java b/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java index 78044ae35c5a0..8057684375d69 100644 --- a/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java +++ b/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java @@ -28,7 +28,7 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.url.URLRepository; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -88,7 +88,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java b/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java index 2dc26f665633c..6cad1e057ef74 100644 --- a/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java +++ b/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java @@ -22,7 +22,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -76,7 +76,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java index db119761641aa..f425de279129b 100644 --- a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java @@ -26,7 +26,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.Scheduler; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -92,7 +92,7 @@ public Collection createComponents( final NamedWriteableRegistry namedWriteableRegistry, final IndexNameExpressionResolver expressionResolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java index 3b7318c8d20f2..b2bfbdb976eb9 100644 --- a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java +++ b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java @@ -12,7 +12,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.hamcrest.OptionalMatchers; import org.elasticsearch.threadpool.Scheduler; @@ -60,28 +60,28 @@ public class SystemdPluginTests extends ESTestCase { public void testIsEnabled() { final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.TRUE.toString()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); assertTrue(plugin.isEnabled()); assertNotNull(plugin.extender()); } public void testIsNotPackageDistribution() { final SystemdPlugin plugin = new SystemdPlugin(false, randomNonPackageBuildType, Boolean.TRUE.toString()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); assertFalse(plugin.isEnabled()); assertNull(plugin.extender()); } public void testIsImplicitlyNotEnabled() { final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, null); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); assertFalse(plugin.isEnabled()); assertNull(plugin.extender()); } public void testIsExplicitlyNotEnabled() { final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.FALSE.toString()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); assertFalse(plugin.isEnabled()); assertNull(plugin.extender()); } @@ -169,7 +169,7 @@ int sd_notify(final int unset_environment, final String state) { } }; - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null); + plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null); if (Boolean.TRUE.toString().equals(esSDNotify)) { assertNotNull(plugin.extender()); } else { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java index 58b824e4676f2..5731163def260 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java @@ -37,7 +37,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -415,7 +415,7 @@ public Collection createComponents( final NamedWriteableRegistry namedWriteableRegistry, final IndexNameExpressionResolver expressionResolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java index 448bfdb301aeb..aff04ca521844 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java @@ -22,7 +22,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -77,7 +77,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { @@ -96,7 +96,7 @@ public Collection createComponents( namedWriteableRegistry, expressionResolver, repositoriesServiceSupplier, - tracer, + telemetryProvider, allocationService, indicesService ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java index de42c95d79476..11d32bb231a01 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java @@ -27,7 +27,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -107,7 +107,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java index a4333d51163dc..707644e28228a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java @@ -27,7 +27,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.threadpool.ThreadPool; @@ -136,7 +136,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java index 60a1e7b5bddbc..165242128ca8a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java @@ -38,7 +38,7 @@ import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -405,7 +405,7 @@ public Collection createComponents( final NamedWriteableRegistry namedWriteableRegistry, final IndexNameExpressionResolver expressionResolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java index bbb6ae9d16fc5..fb0e3478c2cde 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java @@ -22,7 +22,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.threadpool.ThreadPool; @@ -75,7 +75,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java index f5453c8ddf1f1..f8d2b0e464b72 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java @@ -27,7 +27,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -97,7 +97,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 4e1d6ece3602b..a198fe0233d84 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -758,7 +758,7 @@ protected Node( namedWriteableRegistry, clusterModule.getIndexNameExpressionResolver(), repositoriesServiceReference::get, - tracer, + telemetryProvider, clusterModule.getAllocationService(), indicesService ) diff --git a/server/src/main/java/org/elasticsearch/plugins/Plugin.java b/server/src/main/java/org/elasticsearch/plugins/Plugin.java index 83e620aa30d12..bd5d8e9220517 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -26,7 +26,7 @@ import org.elasticsearch.indices.IndicesService; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -81,7 +81,7 @@ public abstract class Plugin implements Closeable { * @param indexNameExpressionResolver A service that resolves expression to index and alias names * @param repositoriesServiceSupplier A supplier for the service that manages snapshot repositories; will return null when this method * is called, but will return the repositories service once the node is initialized. - * @param tracer An interface for distributed tracing + * @param telemetryProvider An interface for distributed tracing * @param allocationService A service to manage shard allocation in the cluster * @param indicesService A service to manage indices in the cluster */ @@ -97,7 +97,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/test/java/org/elasticsearch/node/NodeTests.java b/server/src/test/java/org/elasticsearch/node/NodeTests.java index 87f5fc6e990f6..24bbca6ddf512 100644 --- a/server/src/test/java/org/elasticsearch/node/NodeTests.java +++ b/server/src/test/java/org/elasticsearch/node/NodeTests.java @@ -47,7 +47,7 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.MockHttpTransport; @@ -454,7 +454,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java index 693b7b0fb0981..c5e974c4abcfd 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java @@ -32,7 +32,7 @@ import org.elasticsearch.ingest.Processor; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.PrivilegedOperations; import org.elasticsearch.test.compiler.InMemoryJavaCompiler; @@ -271,7 +271,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java index 2ff2b655cc21a..cc68a3e3ba0cd 100644 --- a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java +++ b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java @@ -31,7 +31,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -75,7 +75,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java b/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java index 385778abdbc5c..9b9f464d8dff3 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java +++ b/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java @@ -32,7 +32,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -88,7 +88,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java index 595dd48aaf32b..9360f97990c82 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java @@ -25,7 +25,7 @@ import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -187,7 +187,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java b/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java index 9d402eef3b55e..a0e6d0daac322 100644 --- a/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java +++ b/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java @@ -23,7 +23,7 @@ import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -73,7 +73,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java index cd95958e31b3e..e2fc1707c0499 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java @@ -36,7 +36,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -119,7 +119,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java index 63fcabb98672a..a139b9a55f1be 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java @@ -50,7 +50,7 @@ import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -189,7 +189,7 @@ public Collection createComponents( final NamedWriteableRegistry namedWriteableRegistry, final IndexNameExpressionResolver expressionResolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java index 721b909e1d3c7..1c8881637d4c6 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java @@ -89,7 +89,7 @@ import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -333,7 +333,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java index d9495afebcab8..afc64140004c7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java @@ -90,6 +90,7 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.snapshots.Snapshot; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -204,7 +205,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { @@ -221,7 +222,7 @@ public Collection createComponents( namedWriteableRegistry, expressionResolver, repositoriesServiceSupplier, - tracer, + telemetryProvider, allocationService, indicesService ) @@ -241,7 +242,7 @@ public Collection createComponents( namedWriteableRegistry, expressionResolver, repositoriesServiceSupplier, - tracer, + telemetryProvider, allocationService, indicesService ) diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 117c7d1265234..349ff1042d3a9 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -29,7 +29,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -100,7 +100,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java index dcb9e53f1a671..5fb20a883560f 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java @@ -35,7 +35,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -201,7 +201,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 19ad17d517cc9..ad2c033b0ee0c 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -35,7 +35,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -209,7 +209,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java index d137f6e719ee3..f10480e9f64a7 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java @@ -30,7 +30,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -114,7 +114,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { @@ -130,7 +130,7 @@ public Collection createComponents( namedWriteableRegistry, indexNameExpressionResolver, repositoriesServiceSupplier, - tracer, + telemetryProvider, allocationService, indicesService ); diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java index 3a18983c8d5a5..d65a81db11266 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java @@ -34,7 +34,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -83,7 +83,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java index 3eec19961d597..783f5550a00af 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java @@ -39,7 +39,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -89,7 +89,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java index 1a088f9aa1de7..0cfa3c43b6b5a 100644 --- a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java +++ b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java @@ -43,7 +43,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -112,7 +112,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java index 3261ac9d9bdb5..943394f326653 100644 --- a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java +++ b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java @@ -32,7 +32,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -98,7 +98,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java index 18bf02fdfd8a1..06ac4673264f3 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java @@ -26,7 +26,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -74,7 +74,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java index 6cd807db27566..6409c2b72e1f1 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java @@ -38,7 +38,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -166,7 +166,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java index aa3f6e6634678..a5b0754a7f178 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java @@ -33,7 +33,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -116,7 +116,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index 9b8009e234911..d527f998a1ee8 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -77,7 +77,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -907,7 +907,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java index 2189d759842f4..47e24b60896da 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java @@ -34,7 +34,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -130,7 +130,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java index 43d7bbdec4178..8808482d2256f 100644 --- a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java +++ b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java @@ -57,7 +57,7 @@ import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotRestoreException; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -102,7 +102,7 @@ public Collection createComponents( final NamedWriteableRegistry registry, final IndexNameExpressionResolver resolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java index 037f57b36d547..9ef887ecf5639 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java @@ -33,7 +33,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -90,7 +90,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java index fd28dd9048275..acd9ad9e85f50 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java @@ -70,7 +70,7 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.snapshots.SearchableSnapshotsSettings; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -331,7 +331,7 @@ public Collection createComponents( final NamedWriteableRegistry registry, final IndexNameExpressionResolver resolver, final Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 9a16785c39dfc..39b62da317b9b 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -90,6 +90,7 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.internal.ShardSearchRequest; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; @@ -635,7 +636,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java index c8838194f461d..6c5e1e6af69ce 100644 --- a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java +++ b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java @@ -40,7 +40,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -146,7 +146,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java index 55af966b4d7e1..754ba7970eaa2 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java @@ -28,7 +28,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -53,7 +53,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java index aa39ec1968ff0..ba743005bf2df 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java @@ -37,7 +37,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -135,7 +135,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java index 6b0500162b567..8f39ce939a51c 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java @@ -31,7 +31,7 @@ import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -99,7 +99,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java index 8c16ad6057706..de0858e59900c 100644 --- a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java +++ b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java @@ -20,7 +20,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -55,7 +55,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java index 05546d9b1345b..c1964448c2662 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java @@ -49,7 +49,7 @@ import org.elasticsearch.rest.RestHandler; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; @@ -241,7 +241,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java b/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java index b6b3a709924aa..1fee6eda5ad6d 100644 --- a/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java +++ b/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java @@ -36,7 +36,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportException; @@ -96,7 +96,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java index e9a8e04b57c73..0c8690cc62e5f 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java @@ -54,7 +54,7 @@ import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.TemplateScript; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; @@ -317,7 +317,7 @@ public Collection createComponents( NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier, - Tracer tracer, + TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService ) { diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java index a427b8bfcfee2..e1f1933242414 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java @@ -15,7 +15,7 @@ import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.TestIndexNameExpressionResolver; import org.elasticsearch.plugins.internal.DocumentParsingObserver; -import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.threadpool.ExecutorBuilder; @@ -75,7 +75,7 @@ public void testWatcherDisabledTests() throws Exception { // also no component creation if not enabled assertThat( - watcher.createComponents(null, null, null, null, null, null, null, null, null, null, null, Tracer.NOOP, null, null), + watcher.createComponents(null, null, null, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null), hasSize(0) ); From 4da2d31390c7233b1d1209411601eeba22b75093 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Fri, 22 Sep 2023 08:58:05 -0400 Subject: [PATCH 035/155] [main] [DOCS] Fix typo in query_cache.asciidoc (#99713) (#99810) Co-authored-by: Joseph AFARI <71259267+joeafari@users.noreply.github.com> --- docs/reference/modules/indices/query_cache.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/modules/indices/query_cache.asciidoc b/docs/reference/modules/indices/query_cache.asciidoc index 59c7e8c6ce3d4..12fe44754d552 100644 --- a/docs/reference/modules/indices/query_cache.asciidoc +++ b/docs/reference/modules/indices/query_cache.asciidoc @@ -2,7 +2,7 @@ === Node query cache settings The results of queries used in the filter context are cached in the node query -cache for fast lookup. There is one queries cache per node that is shared by all +cache for fast lookup. There is one query cache per node that is shared by all shards. The cache uses an LRU eviction policy: when the cache is full, the least recently used query results are evicted to make way for new data. You cannot inspect the contents of the query cache. From ed8ea1f20640f485e87d915bb9240346e8f59dc0 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Fri, 22 Sep 2023 09:11:29 -0400 Subject: [PATCH 036/155] [main] [DOCS] Time series indices support non-metric/dimension fields (#99709) (#99811) Co-authored-by: James Rodewig Co-authored-by: Gilad Gal --- docs/reference/data-streams/tsds.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/data-streams/tsds.asciidoc b/docs/reference/data-streams/tsds.asciidoc index d70f314205c65..9c3151a509b14 100644 --- a/docs/reference/data-streams/tsds.asciidoc +++ b/docs/reference/data-streams/tsds.asciidoc @@ -56,6 +56,8 @@ documents, the document `_id` is a hash of the document's dimensions and * A TSDS uses <>, and as a result is subject to a number of <>. +NOTE: A time series index can contain fields other than dimensions or metrics. + [discrete] [[time-series]] === What is a time series? From dd1eb2056b6b2b91a32e9add3f0f97c54f7f4dbb Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 22 Sep 2023 23:19:28 +1000 Subject: [PATCH 037/155] Wait for cluster to recover before resolving index template (#99797) This PR makes bulk index action wait for cluster to recover before resolving index templates so that ingest pipelines are correctly processed when the cluster is recovering. Resolves: #49499 Supercedes: #46085 --- docs/changelog/99797.yaml | 5 ++ .../ingest/common/IngestRestartIT.java | 90 +++++++++++++++++++ .../action/bulk/TransportBulkAction.java | 48 +++++++++- ...ActionIndicesThatCannotBeCreatedTests.java | 2 + .../bulk/TransportBulkActionIngestTests.java | 2 + 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/99797.yaml diff --git a/docs/changelog/99797.yaml b/docs/changelog/99797.yaml new file mode 100644 index 0000000000000..e46d4501291b5 --- /dev/null +++ b/docs/changelog/99797.yaml @@ -0,0 +1,5 @@ +pr: 99797 +summary: Wait for cluster to recover before resolving index template +area: CRUD +type: bug +issues: [] diff --git a/modules/ingest-common/src/internalClusterTest/java/org/elasticsearch/ingest/common/IngestRestartIT.java b/modules/ingest-common/src/internalClusterTest/java/org/elasticsearch/ingest/common/IngestRestartIT.java index acd0ed59da137..f2cebfc2569d7 100644 --- a/modules/ingest-common/src/internalClusterTest/java/org/elasticsearch/ingest/common/IngestRestartIT.java +++ b/modules/ingest-common/src/internalClusterTest/java/org/elasticsearch/ingest/common/IngestRestartIT.java @@ -7,15 +7,22 @@ */ package org.elasticsearch.ingest.common; +import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.action.support.WriteRequest; +import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Strings; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.ingest.IngestStats; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.MockScriptEngine; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.test.ESIntegTestCase; @@ -24,6 +31,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -262,4 +270,86 @@ public void testWithDedicatedIngestNode() throws Exception { assertThat(source.get("y"), equalTo(0)); } + public void testDefaultPipelineWaitForClusterStateRecovered() throws Exception { + internalCluster().startNode(); + + final var pipeline = new BytesArray(""" + { + "processors" : [ + { + "set": { + "field": "value", + "value": 42 + } + } + ] + }"""); + final TimeValue timeout = TimeValue.timeValueSeconds(10); + client().admin().cluster().preparePutPipeline("test_pipeline", pipeline, XContentType.JSON).get(timeout); + client().admin().indices().preparePutTemplate("pipeline_template").setPatterns(Collections.singletonList("*")).setSettings(""" + { + "index" : { + "default_pipeline" : "test_pipeline" + } + } + """, XContentType.JSON).get(timeout); + + internalCluster().fullRestart(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) { + return Settings.builder().put(GatewayService.RECOVER_AFTER_DATA_NODES_SETTING.getKey(), "2").build(); + } + + @Override + public boolean validateClusterForming() { + return randomBoolean(); + } + }); + + // this one should fail + assertThat( + expectThrows( + ClusterBlockException.class, + () -> client().prepareIndex("index") + .setId("fails") + .setSource("x", 1) + .setTimeout(TimeValue.timeValueMillis(100)) // 100ms, to fail quickly + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(timeout) + ).getMessage(), + equalTo("blocked by: [SERVICE_UNAVAILABLE/1/state not recovered / initialized];") + ); + + // but this one should pass since it has a longer timeout + final PlainActionFuture future = new PlainActionFuture<>(); + client().prepareIndex("index") + .setId("passes1") + .setSource("x", 2) + .setTimeout(TimeValue.timeValueSeconds(60)) // wait for second node to start in below + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .execute(future); + + // so the cluster state can be recovered + internalCluster().startNode(Settings.builder().put(GatewayService.RECOVER_AFTER_DATA_NODES_SETTING.getKey(), "1")); + ensureYellow("index"); + + final IndexResponse indexResponse = future.actionGet(timeout); + assertThat(indexResponse.status(), equalTo(RestStatus.CREATED)); + assertThat(indexResponse.getResult(), equalTo(DocWriteResponse.Result.CREATED)); + + client().prepareIndex("index").setId("passes2").setSource("x", 3).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + + // successfully indexed documents should have the value field set by the pipeline + Map source = client().prepareGet("index", "passes1").get(timeout).getSource(); + assertThat(source.get("x"), equalTo(2)); + assertThat(source.get("value"), equalTo(42)); + + source = client().prepareGet("index", "passes2").get(timeout).getSource(); + assertThat(source.get("x"), equalTo(3)); + assertThat(source.get("value"), equalTo(42)); + + // and make sure this failed doc didn't get through + source = client().prepareGet("index", "fails").get(timeout).getSource(); + assertNull(source); + } } diff --git a/server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java b/server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java index 6635323a2ef2c..13d10be86bd68 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java @@ -192,7 +192,7 @@ public static ActionListe @Override protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener listener) { /* - * This is called on the Transport tread so we can check the indexing + * This is called on the Transport thread so we can check the indexing * memory pressure *quickly* but we don't want to keep the transport * thread busy. Then, as soon as we have the indexing pressure in we fork * to one of the write thread pools. We do this because juggling the @@ -212,6 +212,52 @@ protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener releasingListener = ActionListener.runBefore(listener, releasable::close); final String executorName = isOnlySystem ? Names.SYSTEM_WRITE : Names.WRITE; + ensureClusterStateThenForkAndExecute(task, bulkRequest, executorName, releasingListener); + } + + private void ensureClusterStateThenForkAndExecute( + Task task, + BulkRequest bulkRequest, + String executorName, + ActionListener releasingListener + ) { + final ClusterState initialState = clusterService.state(); + final ClusterBlockException blockException = initialState.blocks().globalBlockedException(ClusterBlockLevel.WRITE); + if (blockException != null) { + if (false == blockException.retryable()) { + releasingListener.onFailure(blockException); + return; + } + logger.trace("cluster is blocked, waiting for it to recover", blockException); + final ClusterStateObserver clusterStateObserver = new ClusterStateObserver( + initialState, + clusterService, + bulkRequest.timeout(), + logger, + threadPool.getThreadContext() + ); + clusterStateObserver.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + forkAndExecute(task, bulkRequest, executorName, releasingListener); + } + + @Override + public void onClusterServiceClose() { + releasingListener.onFailure(new NodeClosedException(clusterService.localNode())); + } + + @Override + public void onTimeout(TimeValue timeout) { + releasingListener.onFailure(blockException); + } + }, newState -> false == newState.blocks().hasGlobalBlockWithLevel(ClusterBlockLevel.WRITE)); + } else { + forkAndExecute(task, bulkRequest, executorName, releasingListener); + } + } + + private void forkAndExecute(Task task, BulkRequest bulkRequest, String executorName, ActionListener releasingListener) { threadPool.executor(Names.WRITE).execute(new ActionRunnable<>(releasingListener) { @Override protected void doRun() { diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java index 1b0c24664be31..618a33821c156 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIndicesThatCannotBeCreatedTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -95,6 +96,7 @@ private void indicesThatCannotBeCreatedTestCase( ClusterState state = mock(ClusterState.class); when(state.getMetadata()).thenReturn(Metadata.EMPTY_METADATA); when(state.metadata()).thenReturn(Metadata.EMPTY_METADATA); + when(state.blocks()).thenReturn(mock(ClusterBlocks.class)); when(clusterService.state()).thenReturn(state); when(clusterService.getSettings()).thenReturn(Settings.EMPTY); diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java index 86146e41f87e1..0168eb0488a5b 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateApplier; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.AliasMetadata; import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.metadata.IndexMetadata; @@ -218,6 +219,7 @@ public void setupAction() { .build(); when(state.getMetadata()).thenReturn(metadata); when(state.metadata()).thenReturn(metadata); + when(state.blocks()).thenReturn(mock(ClusterBlocks.class)); when(clusterService.state()).thenReturn(state); doAnswer(invocation -> { ClusterChangedEvent event = mock(ClusterChangedEvent.class); From 21d9de04a1c00b5c8524ae0639909c134ce0c69f Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:30:34 +0100 Subject: [PATCH 038/155] Add a Factory for building blocks and Vectors (#99657) This commit adds a BlockFactory - an extra level of indirection when building blocks. The factory couples circuit breaking when building, allowing for incrementing the breaker as blocks and Vectors are built. This PR adds the infrastructure to allow us to move the operators and implementations over to the factory, rather than actually moving all there at once. --- .../compute/operator/AggregatorBenchmark.java | 12 +- .../compute/operator/EvalBenchmark.java | 7 +- x-pack/plugin/esql/compute/build.gradle | 2 +- .../compute/data/BooleanArrayBlock.java | 17 +- .../compute/data/BooleanArrayVector.java | 10 +- .../compute/data/BooleanBigArrayVector.java | 6 +- .../compute/data/BooleanBlock.java | 16 +- .../compute/data/BooleanBlockBuilder.java | 22 +- .../compute/data/BooleanVector.java | 8 +- .../compute/data/BooleanVectorBlock.java | 7 +- .../compute/data/BooleanVectorBuilder.java | 25 +- .../compute/data/BytesRefArrayBlock.java | 19 +- .../compute/data/BytesRefArrayVector.java | 7 +- .../compute/data/BytesRefBlock.java | 16 +- .../compute/data/BytesRefBlockBuilder.java | 25 +- .../compute/data/BytesRefVector.java | 8 +- .../compute/data/BytesRefVectorBlock.java | 7 +- .../compute/data/BytesRefVectorBuilder.java | 24 +- .../compute/data/ConstantBooleanVector.java | 8 +- .../compute/data/ConstantBytesRefVector.java | 8 +- .../compute/data/ConstantDoubleVector.java | 8 +- .../compute/data/ConstantIntVector.java | 8 +- .../compute/data/ConstantLongVector.java | 8 +- .../compute/data/DoubleArrayBlock.java | 17 +- .../compute/data/DoubleArrayVector.java | 10 +- .../compute/data/DoubleBigArrayVector.java | 6 +- .../compute/data/DoubleBlock.java | 16 +- .../compute/data/DoubleBlockBuilder.java | 22 +- .../compute/data/DoubleVector.java | 8 +- .../compute/data/DoubleVectorBlock.java | 7 +- .../compute/data/DoubleVectorBuilder.java | 25 +- .../compute/data/FilterBooleanVector.java | 7 +- .../compute/data/FilterBytesRefVector.java | 7 +- .../compute/data/FilterDoubleVector.java | 7 +- .../compute/data/FilterIntVector.java | 7 +- .../compute/data/FilterLongVector.java | 7 +- .../compute/data/IntArrayBlock.java | 17 +- .../compute/data/IntArrayVector.java | 10 +- .../compute/data/IntBigArrayVector.java | 6 +- .../elasticsearch/compute/data/IntBlock.java | 16 +- .../compute/data/IntBlockBuilder.java | 22 +- .../elasticsearch/compute/data/IntVector.java | 8 +- .../compute/data/IntVectorBlock.java | 7 +- .../compute/data/IntVectorBuilder.java | 25 +- .../compute/data/LongArrayBlock.java | 17 +- .../compute/data/LongArrayVector.java | 10 +- .../compute/data/LongBigArrayVector.java | 6 +- .../elasticsearch/compute/data/LongBlock.java | 16 +- .../compute/data/LongBlockBuilder.java | 22 +- .../compute/data/LongVector.java | 8 +- .../compute/data/LongVectorBlock.java | 7 +- .../compute/data/LongVectorBuilder.java | 25 +- .../compute/src/main/java/module-info.java | 2 + .../blockhash/BytesRefLongBlockHash.java | 1 + .../aggregation/blockhash/IntBlockHash.java | 2 + .../aggregation/blockhash/LongBlockHash.java | 3 +- .../compute/data/AbstractArrayBlock.java | 14 +- .../compute/data/AbstractBlock.java | 13 +- .../compute/data/AbstractBlockBuilder.java | 20 +- .../compute/data/AbstractFilterBlock.java | 5 + .../compute/data/AbstractFilterVector.java | 4 +- .../compute/data/AbstractVector.java | 15 +- .../compute/data/AbstractVectorBlock.java | 4 +- .../compute/data/AbstractVectorBuilder.java | 18 + .../org/elasticsearch/compute/data/Block.java | 12 +- .../compute/data/BlockFactory.java | 317 ++++++++++ .../compute/data/BlockFactoryParameters.java | 23 + .../compute/data/ConstantNullBlock.java | 9 +- .../elasticsearch/compute/data/DocBlock.java | 2 +- .../elasticsearch/compute/data/DocVector.java | 2 +- .../elasticsearch/compute/data/Vector.java | 3 + .../compute/data/X-ArrayBlock.java.st | 29 +- .../compute/data/X-ArrayVector.java.st | 19 +- .../compute/data/X-BigArrayVector.java.st | 6 +- .../compute/data/X-Block.java.st | 16 +- .../compute/data/X-BlockBuilder.java.st | 34 +- .../compute/data/X-ConstantVector.java.st | 8 +- .../compute/data/X-FilterVector.java.st | 7 +- .../compute/data/X-Vector.java.st | 8 +- .../compute/data/X-VectorBlock.java.st | 7 +- .../compute/data/X-VectorBuilder.java.st | 41 +- .../compute/operator/DriverContext.java | 11 +- .../operator/ThrowingDriverContext.java | 8 +- .../operator/topn/ResultBuilderForDoc.java | 8 +- .../elasticsearch/compute/OperatorTests.java | 16 +- .../compute/TestBlockFactoryParameters.java | 42 ++ ...untDistinctIntAggregatorFunctionTests.java | 5 +- ...ntDistinctLongAggregatorFunctionTests.java | 5 +- .../GroupingAggregatorFunctionTestCase.java | 8 +- .../SumIntAggregatorFunctionTests.java | 5 +- .../SumLongAggregatorFunctionTests.java | 5 +- .../blockhash/BlockHashRandomizedTests.java | 4 +- .../aggregation/blockhash/BlockHashTests.java | 105 +++- .../compute/data/BasicBlockTests.java | 174 ++++-- .../compute/data/BlockAccountingTests.java | 61 +- .../data/BlockBuilderAppendBlockTests.java | 2 +- .../compute/data/BlockFactoryTests.java | 564 ++++++++++++++++++ .../compute/operator/AnyOperatorTestCase.java | 3 +- .../compute/operator/AsyncOperatorTests.java | 4 +- .../compute/operator/DriverContextTests.java | 6 +- .../compute/operator/OperatorTestCase.java | 10 +- .../compute/operator/RowOperatorTests.java | 4 +- .../SequenceDoubleBlockSourceOperator.java | 4 +- .../SequenceLongBlockSourceOperator.java | 4 +- .../exchange/ExchangeServiceTests.java | 4 +- .../operator/topn/TopNOperatorTests.java | 13 +- ...search.compute.data.BlockFactoryParameters | 8 + .../xpack/esql/lookup/EnrichLookupIT.java | 4 +- .../esql/enrich/EnrichLookupService.java | 8 +- .../esql/planner/LocalExecutionPlanner.java | 16 +- .../xpack/esql/plugin/ComputeService.java | 8 +- .../esql/plugin/EsqlBlockFactoryParams.java | 45 ++ .../esql/plugin/TransportEsqlQueryAction.java | 8 +- ...search.compute.data.BlockFactoryParameters | 8 + .../elasticsearch/xpack/esql/CsvTests.java | 2 + .../function/AbstractFunctionTestCase.java | 4 +- .../xpack/esql/planner/EvalMapperTests.java | 4 +- .../planner/LocalExecutionPlannerTests.java | 2 + 118 files changed, 2089 insertions(+), 393 deletions(-) create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactoryParameters.java create mode 100644 x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/TestBlockFactoryParameters.java create mode 100644 x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java create mode 100644 x-pack/plugin/esql/compute/src/test/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlBlockFactoryParams.java create mode 100644 x-pack/plugin/esql/src/main/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/AggregatorBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/AggregatorBenchmark.java index 75fe76ea8fbb0..545960c7003ab 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/AggregatorBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/AggregatorBenchmark.java @@ -9,6 +9,7 @@ package org.elasticsearch.benchmark.compute.operator; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; import org.elasticsearch.compute.aggregation.AggregatorMode; @@ -23,6 +24,7 @@ import org.elasticsearch.compute.aggregation.SumLongAggregatorFunctionSupplier; import org.elasticsearch.compute.aggregation.blockhash.BlockHash; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DoubleArrayVector; @@ -139,10 +141,11 @@ private static Operator operator(String grouping, String op, String dataType) { ); default -> throw new IllegalArgumentException("unsupported grouping [" + grouping + "]"); }; + DriverContext driverContext = driverContext(); return new HashAggregationOperator( List.of(supplier(op, dataType, groups.size()).groupingAggregatorFactory(AggregatorMode.SINGLE)), () -> BlockHash.build(groups, BIG_ARRAYS, 16 * 1024, false), - new DriverContext(BigArrays.NON_RECYCLING_INSTANCE) + driverContext ); } @@ -576,4 +579,11 @@ private static void run(String grouping, String op, String blockType, int opCoun operator.finish(); checkExpected(grouping, op, blockType, dataType, operator.getOutput(), opCount); } + + static DriverContext driverContext() { + return new DriverContext( + BigArrays.NON_RECYCLING_INSTANCE, + BlockFactory.getInstance(new NoopCircuitBreaker("noop"), BigArrays.NON_RECYCLING_INSTANCE) + ); + } } diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java index 909bf16815a0d..82c9416515d24 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java @@ -8,8 +8,10 @@ package org.elasticsearch.benchmark.compute.operator; +import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.data.IntBlock; @@ -262,6 +264,9 @@ private static void run(String operation) { } static DriverContext driverContext() { - return new DriverContext(BigArrays.NON_RECYCLING_INSTANCE); + return new DriverContext( + BigArrays.NON_RECYCLING_INSTANCE, + BlockFactory.getInstance(new NoopCircuitBreaker("noop"), BigArrays.NON_RECYCLING_INSTANCE) + ); } } diff --git a/x-pack/plugin/esql/compute/build.gradle b/x-pack/plugin/esql/compute/build.gradle index 6058770f25d1b..cd4b131a5b0fe 100644 --- a/x-pack/plugin/esql/compute/build.gradle +++ b/x-pack/plugin/esql/compute/build.gradle @@ -50,7 +50,7 @@ tasks.named('stringTemplates').configure { var longProperties = prop("Long", "long", "LONG", "Long.BYTES") var doubleProperties = prop("Double", "double", "DOUBLE", "Double.BYTES") var bytesRefProperties = prop("BytesRef", "BytesRef", "BYTES_REF", "org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_OBJECT_REF") - var booleanProperties = prop("Boolean", "boolean", "BOOLEAN", "Boolean.BYTES") + var booleanProperties = prop("Boolean", "boolean", "BOOLEAN", "Byte.BYTES") // primitive vectors File vectorInputFile = new File("${projectDir}/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st") template { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java index 53986bf693122..fafe1ce5a0416 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java @@ -24,7 +24,18 @@ public final class BooleanArrayBlock extends AbstractArrayBlock implements Boole private final boolean[] values; public BooleanArrayBlock(boolean[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nulls, mvOrdering); + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public BooleanArrayBlock( + boolean[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -58,7 +69,7 @@ public BooleanBlock expand() { return new BooleanArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new BooleanArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new BooleanArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated(boolean[] values, int[] firstValueIndexes, BitSet nullsMask) { @@ -98,6 +109,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java index be89563e1faf3..840f965ff6806 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java @@ -22,7 +22,11 @@ public final class BooleanArrayVector extends AbstractVector implements BooleanV private final boolean[] values; public BooleanArrayVector(boolean[] values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public BooleanArrayVector(boolean[] values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -78,8 +82,4 @@ public String toString() { return getClass().getSimpleName() + "[positions=" + getPositionCount() + ", values=" + Arrays.toString(values) + ']'; } - @Override - public void close() { - // no-op - } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBigArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBigArrayVector.java index 25a34b383a4b4..777914f51aef4 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBigArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBigArrayVector.java @@ -22,7 +22,11 @@ public final class BooleanBigArrayVector extends AbstractVector implements Boole private final BitArray values; public BooleanBigArrayVector(BitArray values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public BooleanBigArrayVector(BitArray values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java index 03f82d7b952cb..f2501d54a4ae3 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java @@ -157,12 +157,24 @@ static int hash(BooleanBlock block) { return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new BooleanBlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newBooleanBlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static BooleanBlock newConstantBlockWith(boolean value, int positions) { - return new ConstantBooleanVector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static BooleanBlock newConstantBlockWith(boolean value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstantBooleanBlockWith(value, positions); } sealed interface Builder extends Block.Builder permits BooleanBlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java index 101998edbd3bd..a7d397fcfb98e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java @@ -17,8 +17,11 @@ final class BooleanBlockBuilder extends AbstractBlockBuilder implements BooleanB private boolean[] values; - BooleanBlockBuilder(int estimatedSize) { - values = new boolean[Math.max(estimatedSize, 2)]; + BooleanBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); + values = new boolean[initialSize]; } @Override @@ -31,6 +34,11 @@ public BooleanBlockBuilder appendBoolean(boolean value) { return this; } + @Override + protected int elementSize() { + return Byte.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -171,17 +179,21 @@ public BooleanBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { @Override public BooleanBlock build() { finish(); + BooleanBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - return new ConstantBooleanVector(values[0], 1).asBlock(); + block = new ConstantBooleanVector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } if (isDense() && singleValued()) { - return new BooleanArrayVector(values, positionCount).asBlock(); + block = new BooleanArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new BooleanArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new BooleanArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java index 1a5687050392e..477c8310e9708 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java @@ -100,8 +100,14 @@ default void writeTo(StreamOutput out) throws IOException { } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new BooleanVectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newBooleanVectorBuilder(estimatedSize); } sealed interface Builder extends Vector.Builder permits BooleanVectorBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java index 6c138b8f56d11..1727b83360cab 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.data; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -16,12 +15,10 @@ */ public final class BooleanVectorBlock extends AbstractVectorBlock implements BooleanBlock { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BooleanVectorBlock.class); - private final BooleanVector vector; BooleanVectorBlock(BooleanVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -52,7 +49,7 @@ public BooleanBlock filter(int... positions) { @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java index cd1f84e03dd14..d9926227e1c60 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java @@ -17,7 +17,10 @@ final class BooleanVectorBuilder extends AbstractVectorBuilder implements Boolea private boolean[] values; - BooleanVectorBuilder(int estimatedSize) { + BooleanVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); values = new boolean[Math.max(estimatedSize, 2)]; } @@ -29,6 +32,11 @@ public BooleanVectorBuilder appendBoolean(boolean value) { return this; } + @Override + protected int elementSize() { + return Byte.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -41,12 +49,17 @@ protected void growValuesArray(int newSize) { @Override public BooleanVector build() { + BooleanVector vector; if (valueCount == 1) { - return new ConstantBooleanVector(values[0], 1); - } - if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { - values = Arrays.copyOf(values, valueCount); + vector = new ConstantBooleanVector(values[0], 1, blockFactory); + } else { + if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { + values = Arrays.copyOf(values, valueCount); + } + vector = new BooleanArrayVector(values, valueCount, blockFactory); } - return new BooleanArrayVector(values, valueCount); + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java index 47c75862c6370..426731ac06798 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; import java.util.BitSet; import java.util.stream.IntStream; @@ -25,7 +26,18 @@ public final class BytesRefArrayBlock extends AbstractArrayBlock implements Byte private final BytesRefArray values; public BytesRefArrayBlock(BytesRefArray values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nulls, mvOrdering); + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public BytesRefArrayBlock( + BytesRefArray values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -59,7 +71,7 @@ public BytesRefBlock expand() { return new BytesRefArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new BytesRefArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new BytesRefArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated(BytesRefArray values, int[] firstValueIndexes, BitSet nullsMask) { @@ -99,6 +111,7 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java index 21422598a0bde..fc32519a6acce 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java @@ -23,7 +23,11 @@ public final class BytesRefArrayVector extends AbstractVector implements BytesRe private final BytesRefArray values; public BytesRefArrayVector(BytesRefArray values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public BytesRefArrayVector(BytesRefArray values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -81,6 +85,7 @@ public String toString() { @Override public void close() { + blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java index 30cce3dbf0bad..e8cf8926d3cd2 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java @@ -162,12 +162,24 @@ static int hash(BytesRefBlock block) { return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new BytesRefBlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newBytesRefBlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static BytesRefBlock newConstantBlockWith(BytesRef value, int positions) { - return new ConstantBytesRefVector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static BytesRefBlock newConstantBlockWith(BytesRef value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstantBytesRefBlockWith(value, positions); } sealed interface Builder extends Block.Builder permits BytesRefBlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java index ed80dcf28fb8e..23c18d2a9ca6e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; /** * Block build of BytesRefBlocks. @@ -19,11 +20,12 @@ final class BytesRefBlockBuilder extends AbstractBlockBuilder implements BytesRe private BytesRefArray values; - BytesRefBlockBuilder(int estimatedSize) { - this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE, blockFactory); } - BytesRefBlockBuilder(int estimatedSize, BigArrays bigArrays) { + BytesRefBlockBuilder(int estimatedSize, BigArrays bigArrays, BlockFactory blockFactory) { + super(blockFactory); values = new BytesRefArray(Math.max(estimatedSize, 2), bigArrays); } @@ -37,6 +39,11 @@ public BytesRefBlockBuilder appendBytesRef(BytesRef value) { return this; } + @Override + protected int elementSize() { + return -1; + } + @Override protected int valuesLength() { return Integer.MAX_VALUE; // allow the BytesRefArray through its own append @@ -185,14 +192,20 @@ public BytesRefBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { @Override public BytesRefBlock build() { finish(); + BytesRefBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - return new ConstantBytesRefVector(values.get(0, new BytesRef()), 1).asBlock(); + block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + Releasables.closeExpectNoException(values); } else { + estimatedBytes += values.ramBytesUsed(); if (isDense() && singleValued()) { - return new BytesRefArrayVector(values, positionCount).asBlock(); + block = new BytesRefArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java index 6201ab4a3728a..ffe0b06d1f430 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java @@ -100,8 +100,14 @@ default void writeTo(StreamOutput out) throws IOException { } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new BytesRefVectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newBytesRefVectorBuilder(estimatedSize); } sealed interface Builder extends Vector.Builder permits BytesRefVectorBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java index 791ea6809de63..3799e9c5b7ef7 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java @@ -8,7 +8,6 @@ package org.elasticsearch.compute.data; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -17,12 +16,10 @@ */ public final class BytesRefVectorBlock extends AbstractVectorBlock implements BytesRefBlock { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BytesRefVectorBlock.class); - private final BytesRefVector vector; BytesRefVectorBlock(BytesRefVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -53,7 +50,7 @@ public BytesRefBlock filter(int... positions) { @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java index 6035cdf5774e4..be753771ac961 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; /** * Block build of BytesRefBlocks. @@ -19,11 +20,12 @@ final class BytesRefVectorBuilder extends AbstractVectorBuilder implements Bytes private BytesRefArray values; - BytesRefVectorBuilder(int estimatedSize) { - this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE, blockFactory); } - BytesRefVectorBuilder(int estimatedSize, BigArrays bigArrays) { + BytesRefVectorBuilder(int estimatedSize, BigArrays bigArrays, BlockFactory blockFactory) { + super(blockFactory); values = new BytesRefArray(Math.max(estimatedSize, 2), bigArrays); } @@ -35,6 +37,11 @@ public BytesRefVectorBuilder appendBytesRef(BytesRef value) { return this; } + @Override + protected int elementSize() { + return -1; + } + @Override protected int valuesLength() { return Integer.MAX_VALUE; // allow the BytesRefArray through its own append @@ -47,9 +54,16 @@ protected void growValuesArray(int newSize) { @Override public BytesRefVector build() { + BytesRefVector vector; if (valueCount == 1) { - return new ConstantBytesRefVector(values.get(0, new BytesRef()), 1); + vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + Releasables.closeExpectNoException(values); + } else { + estimatedBytes = values.ramBytesUsed(); + vector = new BytesRefArrayVector(values, valueCount, blockFactory); } - return new BytesRefArrayVector(values, valueCount); + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java index cae795a614732..7119721811401 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java @@ -20,7 +20,11 @@ public final class ConstantBooleanVector extends AbstractVector implements Boole private final boolean value; public ConstantBooleanVector(boolean value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public ConstantBooleanVector(boolean value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -73,6 +77,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java index 09b8bda0e38ce..caa30a5a2148c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java @@ -21,7 +21,11 @@ public final class ConstantBytesRefVector extends AbstractVector implements Byte private final BytesRef value; public ConstantBytesRefVector(BytesRef value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public ConstantBytesRefVector(BytesRef value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -74,6 +78,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java index b4aa5012ce2a0..be41df1188ea0 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java @@ -20,7 +20,11 @@ public final class ConstantDoubleVector extends AbstractVector implements Double private final double value; public ConstantDoubleVector(double value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public ConstantDoubleVector(double value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -73,6 +77,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java index a1ccf781b18f3..4854db91fe567 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java @@ -20,7 +20,11 @@ public final class ConstantIntVector extends AbstractVector implements IntVector private final int value; public ConstantIntVector(int value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public ConstantIntVector(int value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -73,6 +77,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java index 38672e5df9b04..1f33d97e9c39d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java @@ -20,7 +20,11 @@ public final class ConstantLongVector extends AbstractVector implements LongVect private final long value; public ConstantLongVector(long value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public ConstantLongVector(long value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -73,6 +77,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java index 6a81b10a3b107..c8d7035b31d3b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java @@ -24,7 +24,18 @@ public final class DoubleArrayBlock extends AbstractArrayBlock implements Double private final double[] values; public DoubleArrayBlock(double[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nulls, mvOrdering); + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public DoubleArrayBlock( + double[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -58,7 +69,7 @@ public DoubleBlock expand() { return new DoubleArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new DoubleArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new DoubleArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated(double[] values, int[] firstValueIndexes, BitSet nullsMask) { @@ -98,6 +109,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java index 6f82d60ae1421..44bf852f628ca 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java @@ -22,7 +22,11 @@ public final class DoubleArrayVector extends AbstractVector implements DoubleVec private final double[] values; public DoubleArrayVector(double[] values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public DoubleArrayVector(double[] values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -78,8 +82,4 @@ public String toString() { return getClass().getSimpleName() + "[positions=" + getPositionCount() + ", values=" + Arrays.toString(values) + ']'; } - @Override - public void close() { - // no-op - } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBigArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBigArrayVector.java index 129d4b3c31d93..f97384bc0a4b8 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBigArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBigArrayVector.java @@ -22,7 +22,11 @@ public final class DoubleBigArrayVector extends AbstractVector implements Double private final DoubleArray values; public DoubleBigArrayVector(DoubleArray values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public DoubleBigArrayVector(DoubleArray values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java index cf749f20de9b2..9edd887448938 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java @@ -158,12 +158,24 @@ static int hash(DoubleBlock block) { return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new DoubleBlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newDoubleBlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static DoubleBlock newConstantBlockWith(double value, int positions) { - return new ConstantDoubleVector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static DoubleBlock newConstantBlockWith(double value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstantDoubleBlockWith(value, positions); } sealed interface Builder extends Block.Builder permits DoubleBlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java index ff5b1ddf6e1d2..a97f58f3924b1 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java @@ -17,8 +17,11 @@ final class DoubleBlockBuilder extends AbstractBlockBuilder implements DoubleBlo private double[] values; - DoubleBlockBuilder(int estimatedSize) { - values = new double[Math.max(estimatedSize, 2)]; + DoubleBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); + values = new double[initialSize]; } @Override @@ -31,6 +34,11 @@ public DoubleBlockBuilder appendDouble(double value) { return this; } + @Override + protected int elementSize() { + return Double.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -171,17 +179,21 @@ public DoubleBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { @Override public DoubleBlock build() { finish(); + DoubleBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - return new ConstantDoubleVector(values[0], 1).asBlock(); + block = new ConstantDoubleVector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } if (isDense() && singleValued()) { - return new DoubleArrayVector(values, positionCount).asBlock(); + block = new DoubleArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new DoubleArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new DoubleArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java index 09bdcafffbfe5..8461f36fb9e7a 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java @@ -101,8 +101,14 @@ default void writeTo(StreamOutput out) throws IOException { } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new DoubleVectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newDoubleVectorBuilder(estimatedSize); } sealed interface Builder extends Vector.Builder permits DoubleVectorBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java index 92243b6a53b70..dba00f6b393a9 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.data; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -16,12 +15,10 @@ */ public final class DoubleVectorBlock extends AbstractVectorBlock implements DoubleBlock { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(DoubleVectorBlock.class); - private final DoubleVector vector; DoubleVectorBlock(DoubleVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -52,7 +49,7 @@ public DoubleBlock filter(int... positions) { @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java index 782b43c1bd9e2..8112c5458280f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java @@ -17,7 +17,10 @@ final class DoubleVectorBuilder extends AbstractVectorBuilder implements DoubleV private double[] values; - DoubleVectorBuilder(int estimatedSize) { + DoubleVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); values = new double[Math.max(estimatedSize, 2)]; } @@ -29,6 +32,11 @@ public DoubleVectorBuilder appendDouble(double value) { return this; } + @Override + protected int elementSize() { + return Double.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -41,12 +49,17 @@ protected void growValuesArray(int newSize) { @Override public DoubleVector build() { + DoubleVector vector; if (valueCount == 1) { - return new ConstantDoubleVector(values[0], 1); - } - if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { - values = Arrays.copyOf(values, valueCount); + vector = new ConstantDoubleVector(values[0], 1, blockFactory); + } else { + if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { + values = Arrays.copyOf(values, valueCount); + } + vector = new DoubleArrayVector(values, valueCount, blockFactory); } - return new DoubleArrayVector(values, valueCount); + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanVector.java index 2ff1dcd58dea5..4bf1a3b986eb3 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanVector.java @@ -21,7 +21,7 @@ public final class FilterBooleanVector extends AbstractFilterVector implements B private final BooleanVector vector; FilterBooleanVector(BooleanVector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -90,6 +90,11 @@ private void appendValues(StringBuilder sb) { } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefVector.java index 0d51121d3c0ec..0491a4453617d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefVector.java @@ -22,7 +22,7 @@ public final class FilterBytesRefVector extends AbstractFilterVector implements private final BytesRefVector vector; FilterBytesRefVector(BytesRefVector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -91,6 +91,11 @@ private void appendValues(StringBuilder sb) { } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleVector.java index 1c1c6d1c3db02..50784f09c2b27 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleVector.java @@ -21,7 +21,7 @@ public final class FilterDoubleVector extends AbstractFilterVector implements Do private final DoubleVector vector; FilterDoubleVector(DoubleVector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -90,6 +90,11 @@ private void appendValues(StringBuilder sb) { } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntVector.java index f0833c1878b61..2d0f8551d9ccc 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntVector.java @@ -21,7 +21,7 @@ public final class FilterIntVector extends AbstractFilterVector implements IntVe private final IntVector vector; FilterIntVector(IntVector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -90,6 +90,11 @@ private void appendValues(StringBuilder sb) { } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongVector.java index 5eb987863aa80..d88357deaadfb 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongVector.java @@ -21,7 +21,7 @@ public final class FilterLongVector extends AbstractFilterVector implements Long private final LongVector vector; FilterLongVector(LongVector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -90,6 +90,11 @@ private void appendValues(StringBuilder sb) { } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java index 8df36ff95117b..782e45a6df463 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java @@ -24,7 +24,18 @@ public final class IntArrayBlock extends AbstractArrayBlock implements IntBlock private final int[] values; public IntArrayBlock(int[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nulls, mvOrdering); + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public IntArrayBlock( + int[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -58,7 +69,7 @@ public IntBlock expand() { return new IntArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new IntArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new IntArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated(int[] values, int[] firstValueIndexes, BitSet nullsMask) { @@ -98,6 +109,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java index d8e15408d4492..9f39c74c2e2a3 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java @@ -22,7 +22,11 @@ public final class IntArrayVector extends AbstractVector implements IntVector { private final int[] values; public IntArrayVector(int[] values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public IntArrayVector(int[] values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -78,8 +82,4 @@ public String toString() { return getClass().getSimpleName() + "[positions=" + getPositionCount() + ", values=" + Arrays.toString(values) + ']'; } - @Override - public void close() { - // no-op - } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBigArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBigArrayVector.java index 2058006eb45bb..f3bcc1ef9bb01 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBigArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBigArrayVector.java @@ -22,7 +22,11 @@ public final class IntBigArrayVector extends AbstractVector implements IntVector private final IntArray values; public IntBigArrayVector(IntArray values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public IntBigArrayVector(IntArray values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java index 0fbcbe6c56362..d6f39de6fc938 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java @@ -157,12 +157,24 @@ static int hash(IntBlock block) { return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new IntBlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newIntBlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static IntBlock newConstantBlockWith(int value, int positions) { - return new ConstantIntVector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static IntBlock newConstantBlockWith(int value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstantIntBlockWith(value, positions); } sealed interface Builder extends Block.Builder permits IntBlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java index ba45611a7bdc7..53d379d715c9b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java @@ -17,8 +17,11 @@ final class IntBlockBuilder extends AbstractBlockBuilder implements IntBlock.Bui private int[] values; - IntBlockBuilder(int estimatedSize) { - values = new int[Math.max(estimatedSize, 2)]; + IntBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); + values = new int[initialSize]; } @Override @@ -31,6 +34,11 @@ public IntBlockBuilder appendInt(int value) { return this; } + @Override + protected int elementSize() { + return Integer.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -171,17 +179,21 @@ public IntBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { @Override public IntBlock build() { finish(); + IntBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - return new ConstantIntVector(values[0], 1).asBlock(); + block = new ConstantIntVector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } if (isDense() && singleValued()) { - return new IntArrayVector(values, positionCount).asBlock(); + block = new IntArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new IntArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new IntArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java index 35bab4278d2fd..a6347cb5da70f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java @@ -100,8 +100,14 @@ default void writeTo(StreamOutput out) throws IOException { } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new IntVectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newIntVectorBuilder(estimatedSize); } /** Create a vector for a range of ints. */ diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java index 20499fe900558..b91c4c8dbeefa 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.data; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -16,12 +15,10 @@ */ public final class IntVectorBlock extends AbstractVectorBlock implements IntBlock { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(IntVectorBlock.class); - private final IntVector vector; IntVectorBlock(IntVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -52,7 +49,7 @@ public IntBlock filter(int... positions) { @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java index 9ae625152ce8e..8bf4a4a96c5cb 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java @@ -17,7 +17,10 @@ final class IntVectorBuilder extends AbstractVectorBuilder implements IntVector. private int[] values; - IntVectorBuilder(int estimatedSize) { + IntVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); values = new int[Math.max(estimatedSize, 2)]; } @@ -29,6 +32,11 @@ public IntVectorBuilder appendInt(int value) { return this; } + @Override + protected int elementSize() { + return Integer.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -41,12 +49,17 @@ protected void growValuesArray(int newSize) { @Override public IntVector build() { + IntVector vector; if (valueCount == 1) { - return new ConstantIntVector(values[0], 1); - } - if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { - values = Arrays.copyOf(values, valueCount); + vector = new ConstantIntVector(values[0], 1, blockFactory); + } else { + if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { + values = Arrays.copyOf(values, valueCount); + } + vector = new IntArrayVector(values, valueCount, blockFactory); } - return new IntArrayVector(values, valueCount); + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java index 9a1681a97a27c..5d6c3d2931a85 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java @@ -24,7 +24,18 @@ public final class LongArrayBlock extends AbstractArrayBlock implements LongBloc private final long[] values; public LongArrayBlock(long[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nulls, mvOrdering); + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public LongArrayBlock( + long[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -58,7 +69,7 @@ public LongBlock expand() { return new LongArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new LongArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new LongArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated(long[] values, int[] firstValueIndexes, BitSet nullsMask) { @@ -98,6 +109,6 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java index a9d7cdfb40bf8..b4d467d44af3e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java @@ -22,7 +22,11 @@ public final class LongArrayVector extends AbstractVector implements LongVector private final long[] values; public LongArrayVector(long[] values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public LongArrayVector(long[] values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -78,8 +82,4 @@ public String toString() { return getClass().getSimpleName() + "[positions=" + getPositionCount() + ", values=" + Arrays.toString(values) + ']'; } - @Override - public void close() { - // no-op - } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBigArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBigArrayVector.java index 6db8675a8d69f..3d3ac9ea09e32 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBigArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBigArrayVector.java @@ -22,7 +22,11 @@ public final class LongBigArrayVector extends AbstractVector implements LongVect private final LongArray values; public LongBigArrayVector(LongArray values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public LongBigArrayVector(LongArray values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java index 965c59e3b7f73..d3dc5928cb543 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java @@ -158,12 +158,24 @@ static int hash(LongBlock block) { return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new LongBlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newLongBlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static LongBlock newConstantBlockWith(long value, int positions) { - return new ConstantLongVector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static LongBlock newConstantBlockWith(long value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstantLongBlockWith(value, positions); } sealed interface Builder extends Block.Builder permits LongBlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java index 9834de886d904..a378b382ce31e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java @@ -17,8 +17,11 @@ final class LongBlockBuilder extends AbstractBlockBuilder implements LongBlock.B private long[] values; - LongBlockBuilder(int estimatedSize) { - values = new long[Math.max(estimatedSize, 2)]; + LongBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); + values = new long[initialSize]; } @Override @@ -31,6 +34,11 @@ public LongBlockBuilder appendLong(long value) { return this; } + @Override + protected int elementSize() { + return Long.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -171,17 +179,21 @@ public LongBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { @Override public LongBlock build() { finish(); + LongBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - return new ConstantLongVector(values[0], 1).asBlock(); + block = new ConstantLongVector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } if (isDense() && singleValued()) { - return new LongArrayVector(values, positionCount).asBlock(); + block = new LongArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new LongArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new LongArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java index c931613f9e40c..6e447dde251a4 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java @@ -101,8 +101,14 @@ default void writeTo(StreamOutput out) throws IOException { } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new LongVectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.newLongVectorBuilder(estimatedSize); } sealed interface Builder extends Vector.Builder permits LongVectorBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java index cf8fc931f1351..3b3d13bf9c36a 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.data; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -16,12 +15,10 @@ */ public final class LongVectorBlock extends AbstractVectorBlock implements LongBlock { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(LongVectorBlock.class); - private final LongVector vector; LongVectorBlock(LongVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -52,7 +49,7 @@ public LongBlock filter(int... positions) { @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java index ba4864bdde812..10daed94a966e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java @@ -17,7 +17,10 @@ final class LongVectorBuilder extends AbstractVectorBuilder implements LongVecto private long[] values; - LongVectorBuilder(int estimatedSize) { + LongVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); values = new long[Math.max(estimatedSize, 2)]; } @@ -29,6 +32,11 @@ public LongVectorBuilder appendLong(long value) { return this; } + @Override + protected int elementSize() { + return Long.BYTES; + } + @Override protected int valuesLength() { return values.length; @@ -41,12 +49,17 @@ protected void growValuesArray(int newSize) { @Override public LongVector build() { + LongVector vector; if (valueCount == 1) { - return new ConstantLongVector(values[0], 1); - } - if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { - values = Arrays.copyOf(values, valueCount); + vector = new ConstantLongVector(values[0], 1, blockFactory); + } else { + if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { + values = Arrays.copyOf(values, valueCount); + } + vector = new LongArrayVector(values, valueCount, blockFactory); } - return new LongArrayVector(values, valueCount); + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/java/module-info.java b/x-pack/plugin/esql/compute/src/main/java/module-info.java index 69aa6f5bb217a..3104fb05280eb 100644 --- a/x-pack/plugin/esql/compute/src/main/java/module-info.java +++ b/x-pack/plugin/esql/compute/src/main/java/module-info.java @@ -6,6 +6,8 @@ */ module org.elasticsearch.compute { + uses org.elasticsearch.compute.data.BlockFactoryParameters; + requires org.apache.lucene.core; requires org.elasticsearch.base; requires org.elasticsearch.server; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java index 50fd1bb7b0943..0c5b60f471f8c 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java @@ -74,6 +74,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { } else { new AddBlock(block1, block2, addInput).add(); } + Releasables.closeExpectNoException(block1, block2); } public IntVector add(BytesRefVector vector1, LongVector vector2) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java index 4fcd9735f6158..7e5f3c94b91cb 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java @@ -20,6 +20,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.MultivalueDedupe; import org.elasticsearch.compute.operator.MultivalueDedupeInt; +import org.elasticsearch.core.Releasables; import java.util.BitSet; @@ -52,6 +53,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { } else { addInput.add(0, add(vector)); } + Releasables.closeExpectNoException(block); } private IntVector add(IntVector vector) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java index 5e5b46ae6eda1..b8b66e2197b63 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java @@ -13,7 +13,6 @@ import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; import org.elasticsearch.compute.aggregation.SeenGroupIds; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongArrayBlock; @@ -63,7 +62,7 @@ private IntVector add(LongVector vector) { for (int i = 0; i < vector.getPositionCount(); i++) { groups[i] = Math.toIntExact(hashOrdToGroupNullReserved(longHash.add(vector.getLong(i)))); } - return new IntArrayVector(groups, groups.length); + return vector.blockFactory().newIntArrayVector(groups, groups.length); } private IntBlock add(LongBlock block) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractArrayBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractArrayBlock.java index 8fb91e4a07a5c..fe1ecbec92e5b 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractArrayBlock.java @@ -18,16 +18,22 @@ abstract class AbstractArrayBlock extends AbstractBlock { /** * @param positionCount the number of values in this block */ - protected AbstractArrayBlock(int positionCount, MvOrdering mvOrdering) { - super(positionCount); + protected AbstractArrayBlock(int positionCount, MvOrdering mvOrdering, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.mvOrdering = mvOrdering; } /** * @param positionCount the number of values in this block */ - protected AbstractArrayBlock(int positionCount, @Nullable int[] firstValueIndexes, @Nullable BitSet nullsMask, MvOrdering mvOrdering) { - super(positionCount, firstValueIndexes, nullsMask); + protected AbstractArrayBlock( + int positionCount, + @Nullable int[] firstValueIndexes, + @Nullable BitSet nullsMask, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nullsMask, blockFactory); this.mvOrdering = mvOrdering; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java index 2d8d75f6c3972..cf8a0d9a833ec 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java @@ -21,12 +21,15 @@ abstract class AbstractBlock implements Block { @Nullable protected final BitSet nullsMask; + protected final BlockFactory blockFactory; + /** * @param positionCount the number of values in this block */ - protected AbstractBlock(int positionCount) { + protected AbstractBlock(int positionCount, BlockFactory blockFactory) { assert positionCount >= 0; this.positionCount = positionCount; + this.blockFactory = blockFactory; this.firstValueIndexes = null; this.nullsMask = null; } @@ -34,9 +37,10 @@ protected AbstractBlock(int positionCount) { /** * @param positionCount the number of values in this block */ - protected AbstractBlock(int positionCount, @Nullable int[] firstValueIndexes, @Nullable BitSet nullsMask) { + protected AbstractBlock(int positionCount, @Nullable int[] firstValueIndexes, @Nullable BitSet nullsMask, BlockFactory blockFactory) { assert positionCount >= 0; this.positionCount = positionCount; + this.blockFactory = blockFactory; this.firstValueIndexes = firstValueIndexes; this.nullsMask = nullsMask == null || nullsMask.isEmpty() ? null : nullsMask; assert (firstValueIndexes == null && this.nullsMask == null) == false; @@ -85,4 +89,9 @@ public int nullValuesCount() { public boolean areAllValuesNull() { return nullValuesCount() == getPositionCount(); } + + @Override + public BlockFactory blockFactory() { + return blockFactory; + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java index 88e7b27adf915..a6ad5d1299543 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java @@ -13,6 +13,8 @@ abstract class AbstractBlockBuilder implements Block.Builder { + protected final BlockFactory blockFactory; + protected int[] firstValueIndexes; // lazily initialized, if multi-values protected BitSet nullsMask; // lazily initialized, if sparse @@ -28,7 +30,12 @@ abstract class AbstractBlockBuilder implements Block.Builder { protected Block.MvOrdering mvOrdering = Block.MvOrdering.UNORDERED; - protected AbstractBlockBuilder() {} + /** The number of bytes currently estimated with the breaker. */ + protected long estimatedBytes; + + protected AbstractBlockBuilder(BlockFactory blockFactory) { + this.blockFactory = blockFactory; + } @Override public AbstractBlockBuilder appendNull() { @@ -105,12 +112,16 @@ protected final void finish() { protected abstract void growValuesArray(int newSize); + /** The number of bytes used to represent each value element. */ + protected abstract int elementSize(); + protected final void ensureCapacity() { int valuesLength = valuesLength(); if (valueCount < valuesLength) { return; } int newSize = calculateNewArraySize(valuesLength); + adjustBreaker((long) (newSize - valuesLength) * elementSize()); growValuesArray(newSize); } @@ -119,8 +130,15 @@ static int calculateNewArraySize(int currentSize) { return currentSize + (currentSize >> 1); } + protected void adjustBreaker(long deltaBytes) { + blockFactory.adjustBreaker(deltaBytes, false); + estimatedBytes += deltaBytes; + } + private void setFirstValue(int position, int value) { if (position >= firstValueIndexes.length) { + final int currentSize = firstValueIndexes.length; + adjustBreaker((long) (position + 1 - currentSize) * Integer.BYTES); firstValueIndexes = Arrays.copyOf(firstValueIndexes, position + 1); } firstValueIndexes[position] = value; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java index 6ab1ea2063722..b7ed14f955244 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java @@ -100,6 +100,11 @@ public MvOrdering mvOrdering() { return block.mvOrdering(); } + @Override + public BlockFactory blockFactory() { + return block.blockFactory(); + } + private int mapPosition(int position) { assert assertPosition(position); return positions[position]; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterVector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterVector.java index a74ff44511602..c4f9498670ae9 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterVector.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterVector.java @@ -17,8 +17,8 @@ abstract class AbstractFilterVector extends AbstractVector { protected final int[] positions; - protected AbstractFilterVector(int[] positions) { - super(positions.length); + protected AbstractFilterVector(int[] positions, BlockFactory blockFactory) { + super(positions.length, blockFactory); this.positions = positions; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVector.java index 6b7ef080ae5a3..a0335bb5c24e7 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVector.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVector.java @@ -13,9 +13,11 @@ abstract class AbstractVector implements Vector { private final int positionCount; + protected final BlockFactory blockFactory; - protected AbstractVector(int positionCount) { + protected AbstractVector(int positionCount, BlockFactory blockFactory) { this.positionCount = positionCount; + this.blockFactory = blockFactory; } public final int getPositionCount() { @@ -26,4 +28,15 @@ public final int getPositionCount() { public final Vector getRow(int position) { return filter(position); } + + @Override + public BlockFactory blockFactory() { + return blockFactory; + } + + @Override + public void close() { + blockFactory.adjustBreaker(-ramBytesUsed(), true); + } + } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java index c95a4cfa52757..d83d26cf33831 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java @@ -12,8 +12,8 @@ */ abstract class AbstractVectorBlock extends AbstractBlock { - AbstractVectorBlock(int positionCount) { - super(positionCount); + AbstractVectorBlock(int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java index 08b7e0d5dc10f..49ce276074735 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java @@ -10,17 +10,30 @@ abstract class AbstractVectorBuilder { protected int valueCount; + protected final BlockFactory blockFactory; + + /** The number of bytes currently estimated with the breaker. */ + protected long estimatedBytes; + + protected AbstractVectorBuilder(BlockFactory blockFactory) { + this.blockFactory = blockFactory; + } + /** The length of the internal values array. */ protected abstract int valuesLength(); protected abstract void growValuesArray(int newSize); + /** The number of bytes used to represent each value element. */ + protected abstract int elementSize(); + protected final void ensureCapacity() { int valuesLength = valuesLength(); if (valueCount < valuesLength) { return; } int newSize = calculateNewArraySize(valuesLength); + adjustBreaker((long) (newSize - valuesLength) * elementSize()); growValuesArray(newSize); } @@ -28,4 +41,9 @@ static int calculateNewArraySize(int currentSize) { // trivially, grows array by 50% return currentSize + (currentSize >> 1); } + + protected void adjustBreaker(long deltaBytes) { + blockFactory.adjustBreaker(deltaBytes, false); + estimatedBytes += deltaBytes; + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index d80119ad57fae..1982c937f2a17 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -58,6 +58,9 @@ public interface Block extends Accountable, NamedWriteable, Releasable { */ ElementType elementType(); + /** The block factory associated with this block. */ + BlockFactory blockFactory(); + /** * Returns true if the value stored at the given position is null, false otherwise. * @@ -116,10 +119,15 @@ enum MvOrdering { Block expand(); /** - * {@return a constant null block with the given number of positions}. + * {@return a constant null block with the given number of positions, using the non-breaking block factory}. */ + // Eventually, this should use the GLOBAL breaking instance static Block constantNullBlock(int positions) { - return new ConstantNullBlock(positions); + return constantNullBlock(positions, BlockFactory.getNonBreakingInstance()); + } + + static Block constantNullBlock(int positions, BlockFactory blockFactory) { + return blockFactory.newConstantNullBlock(positions); } interface Builder { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java new file mode 100644 index 0000000000000..ca2c4e7c453d0 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java @@ -0,0 +1,317 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.breaker.NoopCircuitBreaker; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.compute.data.Block.MvOrdering; + +import java.util.BitSet; +import java.util.List; +import java.util.ServiceLoader; + +public class BlockFactory { + + private static final BlockFactory NON_BREAKING = BlockFactory.getInstance( + new NoopCircuitBreaker("noop-esql-breaker"), + BigArrays.NON_RECYCLING_INSTANCE + ); + + private static final BlockFactory GLOBAL = loadGlobalFactory(); + // new BlockFactory(new NoopCircuitBreaker("esql_noop_breaker"), BigArrays.NON_RECYCLING_INSTANCE); + + private static BlockFactory loadGlobalFactory() { + ServiceLoader loader = ServiceLoader.load( + BlockFactoryParameters.class, + BlockFactory.class.getClassLoader() + ); + List> impls = loader.stream().toList(); + if (impls.size() != 1) { + throw new AssertionError("expected exactly one impl, but got:" + impls); + } + BlockFactoryParameters params = impls.get(0).get(); + return new BlockFactory(params.breaker(), params.bigArrays()); + } + + private final CircuitBreaker breaker; + + private final BigArrays bigArrays; + + private BlockFactory(CircuitBreaker breaker, BigArrays bigArrays) { + this.breaker = breaker; + this.bigArrays = bigArrays; + } + + /** + * Returns the global ESQL block factory. + */ + public static BlockFactory getGlobalInstance() { + return GLOBAL; + } + + /** + * Returns the Non-Breaking block factory. + */ + public static BlockFactory getNonBreakingInstance() { + return NON_BREAKING; + } + + public static BlockFactory getInstance(CircuitBreaker breaker, BigArrays bigArrays) { + return new BlockFactory(breaker, bigArrays); + } + + // For testing + public CircuitBreaker breaker() { + return breaker; + } + + // For testing + public BigArrays bigArrays() { + return bigArrays; + } + + /** + * Adjust the circuit breaker with the given delta, if the delta is negative, the breaker will + * be adjusted without tripping. If the data was already created before calling this method, + * and the breaker trips, we add the delta without breaking to account for the created data. + * If the data has not been created yet, we do not add the delta to the breaker if it trips. + */ + void adjustBreaker(final long delta, final boolean isDataAlreadyCreated) { + // checking breaker means potentially tripping, but it doesn't + // have to if the delta is negative + if (delta > 0) { + try { + breaker.addEstimateBytesAndMaybeBreak(delta, ""); + } catch (CircuitBreakingException e) { + if (isDataAlreadyCreated) { + // since we've already created the data, we need to + // add it so closing the stream re-adjusts properly + breaker.addWithoutBreaking(delta); + } + // re-throw the original exception + throw e; + } + } else { + breaker.addWithoutBreaking(delta); + } + } + + /** Pre-adjusts the breaker for the given position count and element type. Returns the pre-adjusted amount. */ + public long preAdjustBreakerForBoolean(int positionCount) { + long bytes = (long) positionCount * Byte.BYTES; + adjustBreaker(bytes, false); + return bytes; + } + + public long preAdjustBreakerForInt(int positionCount) { + long bytes = (long) positionCount * Integer.BYTES; + adjustBreaker(bytes, false); + return bytes; + } + + public long preAdjustBreakerForLong(int positionCount) { + long bytes = (long) positionCount * Long.BYTES; + adjustBreaker(bytes, false); + return bytes; + } + + public long preAdjustBreakerForDouble(int positionCount) { + long bytes = (long) positionCount * Double.BYTES; + adjustBreaker(bytes, false); + return bytes; + } + + public BooleanBlock.Builder newBooleanBlockBuilder(int estimatedSize) { + return new BooleanBlockBuilder(estimatedSize, this); + } + + public BooleanBlock newBooleanArrayBlock( + boolean[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering + ) { + var b = new BooleanArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public BooleanVector.Builder newBooleanVectorBuilder(int estimatedSize) { + return new BooleanVectorBuilder(estimatedSize, this); + } + + public BooleanVector newBooleanArrayVector(boolean[] values, int positionCount) { + return newBooleanArrayVector(values, positionCount, 0L); + } + + public BooleanVector newBooleanArrayVector(boolean[] values, int positionCount, long preAdjustedBytes) { + var b = new BooleanArrayVector(values, positionCount, this); + adjustBreaker(b.ramBytesUsed() - preAdjustedBytes, true); + return b; + } + + public BooleanBlock newConstantBooleanBlockWith(boolean value, int positions) { + var b = new ConstantBooleanVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public IntBlock.Builder newIntBlockBuilder(int estimatedSize) { + return new IntBlockBuilder(estimatedSize, this); + } + + public IntBlock newIntArrayBlock(int[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { + var b = new IntArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public IntVector.Builder newIntVectorBuilder(int estimatedSize) { + return new IntVectorBuilder(estimatedSize, this); + } + + /** + * Creates a new Vector with the given values and positionCount. Equivalent to: + * newIntArrayVector(values, positionCount, 0L); // with zero pre-adjusted bytes + */ + public IntVector newIntArrayVector(int[] values, int positionCount) { + return newIntArrayVector(values, positionCount, 0L); + } + + /** + * Creates a new Vector with the given values and positionCount, where the caller has already + * pre-adjusted a number of bytes with the factory's breaker. + * + * long preAdjustedBytes = blockFactory.preAdjustBreakerForInt(positionCount); + * int[] values = new int[positionCount]; + * for (int i = 0; i < positionCount; i++) { + * values[i] = doWhateverStuff + * } + * var vector = blockFactory.newIntArrayVector(values, positionCount, preAdjustedBytes); + */ + public IntVector newIntArrayVector(int[] values, int positionCount, long preAdjustedBytes) { + var b = new IntArrayVector(values, positionCount, this); + adjustBreaker(b.ramBytesUsed() - preAdjustedBytes, true); + return b; + } + + public IntBlock newConstantIntBlockWith(int value, int positions) { + var b = new ConstantIntVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public LongBlock.Builder newLongBlockBuilder(int estimatedSize) { + return new LongBlockBuilder(estimatedSize, this); + } + + public LongBlock newLongArrayBlock(long[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { + var b = new LongArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public LongVector.Builder newLongVectorBuilder(int estimatedSize) { + return new LongVectorBuilder(estimatedSize, this); + } + + public LongVector newLongArrayVector(long[] values, int positionCount) { + return newLongArrayVector(values, positionCount, 0L); + } + + public LongVector newLongArrayVector(long[] values, int positionCount, long preAdjustedBytes) { + var b = new LongArrayVector(values, positionCount, this); + adjustBreaker(b.ramBytesUsed() - preAdjustedBytes, true); + return b; + } + + public LongBlock newConstantLongBlockWith(long value, int positions) { + var b = new ConstantLongVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public DoubleBlock.Builder newDoubleBlockBuilder(int estimatedSize) { + return new DoubleBlockBuilder(estimatedSize, this); + } + + public DoubleBlock newDoubleArrayBlock( + double[] values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering + ) { + var b = new DoubleArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public DoubleVector.Builder newDoubleVectorBuilder(int estimatedSize) { + return new DoubleVectorBuilder(estimatedSize, this); + } + + public DoubleVector newDoubleArrayVector(double[] values, int positionCount) { + return newDoubleArrayVector(values, positionCount, 0L); + } + + public DoubleVector newDoubleArrayVector(double[] values, int positionCount, long preAdjustedBytes) { + var b = new DoubleArrayVector(values, positionCount, this); + adjustBreaker(b.ramBytesUsed() - preAdjustedBytes, true); + return b; + } + + public DoubleBlock newConstantDoubleBlockWith(double value, int positions) { + var b = new ConstantDoubleVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public BytesRefBlock.Builder newBytesRefBlockBuilder(int estimatedSize) { + return new BytesRefBlockBuilder(estimatedSize, bigArrays, this); + } + + public BytesRefBlock newBytesRefArrayBlock( + BytesRefArray values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering + ) { + var b = new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); + adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); + return b; + } + + public BytesRefVector.Builder newBytesRefVectorBuilder(int estimatedSize) { + return new BytesRefVectorBuilder(estimatedSize, bigArrays, this); + } + + public BytesRefVector newBytesRefArrayVector(BytesRefArray values, int positionCount) { + var b = new BytesRefArrayVector(values, positionCount, this); + adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); + return b; + } + + public BytesRefBlock newConstantBytesRefBlockWith(BytesRef value, int positions) { + var b = new ConstantBytesRefVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } + + public Block newConstantNullBlock(int positions) { + var b = new ConstantNullBlock(positions, this); + adjustBreaker(b.ramBytesUsed(), true); + return b; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactoryParameters.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactoryParameters.java new file mode 100644 index 0000000000000..a9dc11635f8c0 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactoryParameters.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.util.BigArrays; + +/** + * Allows to inject instances of a breaker and bigArrays into the Global block factory. + * The Global factory is somewhat temporary, therefore this interface and its ServiceLoader + * machinery can be removed once the Global factory is removed. + */ +public interface BlockFactoryParameters { + + CircuitBreaker breaker(); + + BigArrays bigArrays(); +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java index 5a5ed16738810..7ad60d89ed72d 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java @@ -22,8 +22,13 @@ public final class ConstantNullBlock extends AbstractBlock { private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantNullBlock.class); + // Eventually, this should use the GLOBAL breaking instance ConstantNullBlock(int positionCount) { - super(positionCount); + this(positionCount, BlockFactory.getNonBreakingInstance()); + } + + ConstantNullBlock(int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); } @Override @@ -121,7 +126,7 @@ public String toString() { @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } static class Builder implements Block.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java index 7a2ea0ddd69c5..433591be5b2bb 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java @@ -23,7 +23,7 @@ public class DocBlock extends AbstractVectorBlock implements Block { private final DocVector vector; DocBlock(DocVector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocVector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocVector.java index eb67d89c3a869..c334e48d74610 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocVector.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocVector.java @@ -47,7 +47,7 @@ public class DocVector extends AbstractVector implements Vector { private int[] shardSegmentDocMapBackwards; public DocVector(IntVector shards, IntVector segments, IntVector docs, Boolean singleSegmentNonDecreasing) { - super(shards.getPositionCount()); + super(shards.getPositionCount(), null); this.shards = shards; this.segments = segments; this.docs = docs; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java index e8636e2a39970..171bdbd62f4d0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java @@ -47,6 +47,9 @@ public interface Vector extends Accountable, Releasable { */ boolean isConstant(); + /** The block factory associated with this vector. */ + BlockFactory blockFactory(); + interface Builder { /** * Builds the block. This method can be called multiple times. diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st index b0fbea3feccd4..dd3a914eae9f7 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import org.apache.lucene.util.RamUsageEstimator; @@ -35,12 +36,19 @@ $else$ private final $type$[] values; $endif$ -$if(BytesRef)$ - public $Type$ArrayBlock(BytesRefArray values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { -$else$ - public $Type$ArrayBlock($type$[] values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { -$endif$ - super(positionCount, firstValueIndexes, nulls, mvOrdering); + public $Type$ArrayBlock($if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, int positionCount, int[] firstValueIndexes, BitSet nulls, MvOrdering mvOrdering) { + this(values, positionCount, firstValueIndexes, nulls, mvOrdering, BlockFactory.getNonBreakingInstance()); + } + + public $Type$ArrayBlock( + $if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, + int positionCount, + int[] firstValueIndexes, + BitSet nulls, + MvOrdering mvOrdering, + BlockFactory blockFactory + ) { + super(positionCount, firstValueIndexes, nulls, mvOrdering, blockFactory); this.values = values; } @@ -79,7 +87,7 @@ $endif$ return new $Type$ArrayVector(values, end).asBlock(); } int[] firstValues = IntStream.range(0, end + 1).toArray(); - return new $Type$ArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED); + return new $Type$ArrayBlock(values, end, firstValues, shiftNullsToExpandedPositions(), MvOrdering.UNORDERED, blockFactory); } public static long ramBytesEstimated($if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, int[] firstValueIndexes, BitSet nullsMask) { @@ -124,6 +132,11 @@ $endif$ @Override public void close() { - // no-op + $if(BytesRef)$ + blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + Releasables.closeExpectNoException(values); + $else$ + blockFactory.adjustBreaker(-ramBytesUsed(), true); + $endif$ } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st index 103e8bc22d9ed..6065e95daaae9 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st @@ -34,12 +34,12 @@ $else$ private final $type$[] values; $endif$ -$if(BytesRef)$ - public $Type$ArrayVector(BytesRefArray values, int positionCount) { -$else$ - public $Type$ArrayVector($type$[] values, int positionCount) { -$endif$ - super(positionCount); + public $Type$ArrayVector($if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, int positionCount) { + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public $Type$ArrayVector($if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } @@ -107,12 +107,11 @@ $else$ $endif$ } +$if(BytesRef)$ @Override public void close() { -$if(BytesRef)$ + blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); Releasables.closeExpectNoException(values); -$else$ - // no-op -$endif$ } +$endif$ } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BigArrayVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BigArrayVector.java.st index 153cf3c039145..82a2aae80c9de 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BigArrayVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BigArrayVector.java.st @@ -22,7 +22,11 @@ public final class $Type$BigArrayVector extends AbstractVector implements $Type$ private final $if(boolean)$Bit$else$$Type$$endif$Array values; public $Type$BigArrayVector($if(boolean)$Bit$else$$Type$$endif$Array values, int positionCount) { - super(positionCount); + this(values, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public $Type$BigArrayVector($if(boolean)$Bit$else$$Type$$endif$Array values, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.values = values; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st index 9588cdbe9c353..3f626e463f428 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st @@ -194,12 +194,24 @@ $endif$ return result; } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newBlockBuilder(int estimatedSize) { - return new $Type$BlockBuilder(estimatedSize); + return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.new$Type$BlockBuilder(estimatedSize); + } + + /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static $Type$Block newConstantBlockWith($type$ value, int positions) { - return new Constant$Type$Vector(value, positions).asBlock(); + return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); + } + + static $Type$Block newConstantBlockWith($type$ value, int positions, BlockFactory blockFactory) { + return blockFactory.newConstant$Type$BlockWith(value, positions); } sealed interface Builder extends Block.Builder permits $Type$BlockBuilder { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st index fc407361c04ba..4d43f25577cc5 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import java.util.Arrays; @@ -25,19 +26,23 @@ final class $Type$BlockBuilder extends AbstractBlockBuilder implements $Type$Blo $if(BytesRef)$ private BytesRefArray values; - BytesRefBlockBuilder(int estimatedSize) { - this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE, blockFactory); } - BytesRefBlockBuilder(int estimatedSize, BigArrays bigArrays) { + BytesRefBlockBuilder(int estimatedSize, BigArrays bigArrays, BlockFactory blockFactory) { + super(blockFactory); values = new BytesRefArray(Math.max(estimatedSize, 2), bigArrays); } $else$ private $type$[] values; - $Type$BlockBuilder(int estimatedSize) { - values = new $type$[Math.max(estimatedSize, 2)]; + $Type$BlockBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); + values = new $type$[initialSize]; } $endif$ @@ -55,6 +60,11 @@ $endif$ return this; } + @Override + protected int elementSize() { + return $if(BytesRef)$-1$else$$BYTES$$endif$; + } + @Override protected int valuesLength() { $if(BytesRef)$ @@ -235,22 +245,28 @@ $endif$ @Override public $Type$Block build() { finish(); + $Type$Block block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { $if(BytesRef)$ - return new ConstantBytesRefVector(values.get(0, new BytesRef()), 1).asBlock(); + block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + Releasables.closeExpectNoException(values); } else { + estimatedBytes += values.ramBytesUsed(); $else$ - return new Constant$Type$Vector(values[0], 1).asBlock(); + block = new Constant$Type$Vector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } $endif$ if (isDense() && singleValued()) { - return new $Type$ArrayVector(values, positionCount).asBlock(); + block = new $Type$ArrayVector(values, positionCount, blockFactory).asBlock(); } else { - return new $Type$ArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering); + block = new $Type$ArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st index 8f6c911dc4ebb..d1b724b37cd83 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st @@ -23,7 +23,11 @@ public final class Constant$Type$Vector extends AbstractVector implements $Type$ private final $type$ value; public Constant$Type$Vector($type$ value, int positionCount) { - super(positionCount); + this(value, positionCount, BlockFactory.getNonBreakingInstance()); + } + + public Constant$Type$Vector($type$ value, int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); this.value = value; } @@ -80,6 +84,6 @@ $endif$ @Override public void close() { - // no-op + blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterVector.java.st index e68fc6838d3f4..9d67e8182fc17 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterVector.java.st @@ -24,7 +24,7 @@ public final class Filter$Type$Vector extends AbstractFilterVector implements $T private final $Type$Vector vector; Filter$Type$Vector($Type$Vector vector, int... positions) { - super(positions); + super(positions, vector.blockFactory()); this.vector = vector; } @@ -102,6 +102,11 @@ $endif$ } } + @Override + public BlockFactory blockFactory() { + return vector.blockFactory(); + } + @Override public void close() { Releasables.closeExpectNoException(vector); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st index f48ad43faefc8..4acb4243c131a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st @@ -142,8 +142,14 @@ $endif$ } } + /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + // Eventually, we want to remove this entirely, always passing an explicit BlockFactory static Builder newVectorBuilder(int estimatedSize) { - return new $Type$VectorBuilder(estimatedSize); + return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return blockFactory.new$Type$VectorBuilder(estimatedSize); } $if(int)$ diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st index 3abc702839118..0f97119d971a4 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st @@ -10,7 +10,6 @@ package org.elasticsearch.compute.data; $if(BytesRef)$ import org.apache.lucene.util.BytesRef; $endif$ -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.core.Releasables; /** @@ -19,12 +18,10 @@ import org.elasticsearch.core.Releasables; */ public final class $Type$VectorBlock extends AbstractVectorBlock implements $Type$Block { - private static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance($Type$VectorBlock.class); - private final $Type$Vector vector; $Type$VectorBlock($Type$Vector vector) { - super(vector.getPositionCount()); + super(vector.getPositionCount(), vector.blockFactory()); this.vector = vector; } @@ -60,7 +57,7 @@ $endif$ @Override public long ramBytesUsed() { - return RAM_BYTES_USED + RamUsageEstimator.sizeOf(vector); + return vector.ramBytesUsed(); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st index 4c4747e949bff..09e95e16c303d 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import java.util.Arrays; @@ -25,18 +26,22 @@ final class $Type$VectorBuilder extends AbstractVectorBuilder implements $Type$V $if(BytesRef)$ private BytesRefArray values; - BytesRefVectorBuilder(int estimatedSize) { - this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVectorBuilder(int estimatedSize, BlockFactory blockFactory) { + this(estimatedSize, BigArrays.NON_RECYCLING_INSTANCE, blockFactory); } - BytesRefVectorBuilder(int estimatedSize, BigArrays bigArrays) { + BytesRefVectorBuilder(int estimatedSize, BigArrays bigArrays, BlockFactory blockFactory) { + super(blockFactory); values = new BytesRefArray(Math.max(estimatedSize, 2), bigArrays); } $else$ private $type$[] values; - $Type$VectorBuilder(int estimatedSize) { + $Type$VectorBuilder(int estimatedSize, BlockFactory blockFactory) { + super(blockFactory); + int initialSize = Math.max(estimatedSize, 2); + adjustBreaker(initialSize); values = new $type$[Math.max(estimatedSize, 2)]; } $endif$ @@ -53,6 +58,11 @@ $endif$ return this; } + @Override + protected int elementSize() { + return $if(BytesRef)$-1$else$$BYTES$$endif$; + } + @Override protected int valuesLength() { $if(BytesRef)$ @@ -73,17 +83,24 @@ $endif$ @Override public $Type$Vector build() { + $Type$Vector vector; if (valueCount == 1) { $if(BytesRef)$ - return new ConstantBytesRefVector(values.get(0, new BytesRef()), 1); - } + vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + Releasables.closeExpectNoException(values); + } else { + estimatedBytes = values.ramBytesUsed(); $else$ - return new Constant$Type$Vector(values[0], 1); - } - if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { - values = Arrays.copyOf(values, valueCount); - } + vector = new Constant$Type$Vector(values[0], 1, blockFactory); + } else { + if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { + values = Arrays.copyOf(values, valueCount); + } $endif$ - return new $Type$ArrayVector(values, valueCount); + vector = new $Type$ArrayVector(values, valueCount, blockFactory); + } + // update the breaker with the actual bytes used. + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java index db60b45f4516c..8743c64db472a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.core.Releasable; import java.util.Collections; @@ -42,15 +43,23 @@ public class DriverContext { private final BigArrays bigArrays; - public DriverContext(BigArrays bigArrays) { + private final BlockFactory blockFactory; + + public DriverContext(BigArrays bigArrays, BlockFactory blockFactory) { Objects.requireNonNull(bigArrays); + Objects.requireNonNull(blockFactory); this.bigArrays = bigArrays; + this.blockFactory = blockFactory; } public BigArrays bigArrays() { return bigArrays; } + public BlockFactory blockFactory() { + return blockFactory; + } + /** A snapshot of the driver context. */ public record Snapshot(Set releasables) {} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ThrowingDriverContext.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ThrowingDriverContext.java index d985d7649ee38..313ec0b682602 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ThrowingDriverContext.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ThrowingDriverContext.java @@ -13,11 +13,12 @@ import org.elasticsearch.common.util.FloatArray; import org.elasticsearch.common.util.IntArray; import org.elasticsearch.common.util.LongArray; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.core.Releasable; public class ThrowingDriverContext extends DriverContext { public ThrowingDriverContext() { - super(new ThrowingBigArrays()); + super(new ThrowingBigArrays(), BlockFactory.getNonBreakingInstance()); } @Override @@ -25,6 +26,11 @@ public BigArrays bigArrays() { throw new AssertionError("should not reach here"); } + @Override + public BlockFactory blockFactory() { + throw new AssertionError("should not reach here"); + } + @Override public boolean addReleasable(Releasable releasable) { throw new AssertionError("should not reach here"); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java index a825b7d160551..166d5be83b474 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java @@ -9,8 +9,8 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DocVector; -import org.elasticsearch.compute.data.IntArrayVector; class ResultBuilderForDoc implements ResultBuilder { private final int[] shards; @@ -40,9 +40,9 @@ public void decodeValue(BytesRef values) { @Override public Block build() { return new DocVector( - new IntArrayVector(shards, position), - new IntArrayVector(segments, position), - new IntArrayVector(docs, position), + BlockFactory.getNonBreakingInstance().newIntArrayVector(shards, position), + BlockFactory.getNonBreakingInstance().newIntArrayVector(segments, position), + BlockFactory.getNonBreakingInstance().newIntArrayVector(docs, position), null ).asBlock(); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java index d7ec9bcaf99ee..04a966b399870 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java @@ -29,6 +29,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; @@ -36,11 +37,11 @@ import org.elasticsearch.compute.aggregation.CountAggregatorFunction; import org.elasticsearch.compute.aggregation.blockhash.BlockHash; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DocBlock; import org.elasticsearch.compute.data.DocVector; import org.elasticsearch.compute.data.ElementType; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; @@ -147,6 +148,10 @@ public void testQueryOperator() throws IOException { // @Repeat(iterations = 1) public void testGroupingWithOrdinals() throws Exception { + DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); + BigArrays bigArrays = driverContext.bigArrays(); + final String gField = "g"; final int numDocs = 2856; // between(100, 10000); final Map expectedCounts = new HashMap<>(); @@ -162,7 +167,6 @@ public void testGroupingWithOrdinals() throws Exception { } writer.commit(); Map actualCounts = new HashMap<>(); - BigArrays bigArrays = bigArrays(); boolean shuffleDocs = randomBoolean(); Operator shuffleDocsOperator = new AbstractPageMappingOperator() { @Override @@ -195,7 +199,7 @@ protected Page process(Page page) { ids.add(docs.getInt(i)); } Collections.shuffle(ids, random()); - docs = new IntArrayVector(ids.stream().mapToInt(n -> n).toArray(), positionCount); + docs = blockFactory.newIntArrayVector(ids.stream().mapToInt(n -> n).toArray(), positionCount); } Block[] blocks = new Block[page.getBlockCount()]; blocks[0] = new DocVector(shards, segments, docs, false).asBlock(); @@ -212,8 +216,6 @@ public String toString() { }; try (DirectoryReader reader = writer.getReader()) { - DriverContext driverContext = driverContext(); - Driver driver = new Driver( driverContext, luceneOperatorFactory(reader, new MatchAllDocsQuery(), LuceneOperator.NO_LIMIT).get(driverContext), @@ -263,7 +265,6 @@ public String toString() { keys.getBytesRef(i, spare); actualCounts.put(BytesRef.deepCopyOf(spare), counts.getLong(i)); } - // System.out.println("HEGO: keys.getPositionCount=" + keys.getPositionCount()); // Releasables.close(keys); }), () -> {} @@ -398,7 +399,8 @@ private BigArrays bigArrays() { * A {@link DriverContext} that won't throw {@link CircuitBreakingException}. */ protected final DriverContext driverContext() { - return new DriverContext(bigArrays()); + var breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + return new DriverContext(bigArrays(), BlockFactory.getInstance(breaker, bigArrays())); } public static void assertDriverContext(DriverContext driverContext) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/TestBlockFactoryParameters.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/TestBlockFactoryParameters.java new file mode 100644 index 0000000000000..8fa38b6864674 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/TestBlockFactoryParameters.java @@ -0,0 +1,42 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute; + +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.compute.data.BlockFactoryParameters; +import org.elasticsearch.indices.breaker.CircuitBreakerService; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TestBlockFactoryParameters implements BlockFactoryParameters { + + final CircuitBreaker breaker; + final BigArrays bigArrays; + + public TestBlockFactoryParameters() { + breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + var breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, breakerService); + } + + @Override + public CircuitBreaker breaker() { + return breaker; + } + + @Override + public BigArrays bigArrays() { + return bigArrays; + } +} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java index 974046469e518..3699a87431937 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java @@ -11,7 +11,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.CannedSourceOperator; @@ -63,10 +63,11 @@ protected void assertOutputFromEmpty(Block b) { public void testRejectsDouble() { DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); try ( Driver d = new Driver( driverContext, - new CannedSourceOperator(Iterators.single(new Page(new DoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), + new CannedSourceOperator(Iterators.single(new Page(blockFactory.newDoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), List.of(simple(nonBreakingBigArrays()).get(driverContext)), new PageConsumerOperator(page -> fail("shouldn't have made it this far")), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java index 04cbe0ed53236..556f9d0ccc462 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java @@ -11,7 +11,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.CannedSourceOperator; @@ -64,10 +64,11 @@ protected void assertOutputFromEmpty(Block b) { public void testRejectsDouble() { DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); try ( Driver d = new Driver( driverContext, - new CannedSourceOperator(Iterators.single(new Page(new DoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), + new CannedSourceOperator(Iterators.single(new Page(blockFactory.newDoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), List.of(simple(nonBreakingBigArrays()).get(driverContext)), new PageConsumerOperator(page -> fail("shouldn't have made it this far")), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java index 23015d066810a..eab6eb30261bd 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java @@ -12,11 +12,11 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BitArray; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.ElementType; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; @@ -513,7 +513,8 @@ public void add(int positionOffset, IntVector groupIds) { seenGroupIds.set(group); chunk[count++] = group; } - delegateAddInput.add(positionOffset + offset, new IntArrayVector(chunk, count)); + BlockFactory blockFactory = driverContext().blockFactory(); // TODO: just for compile + delegateAddInput.add(positionOffset + offset, blockFactory.newIntArrayVector(chunk, count)); } } }; @@ -527,7 +528,8 @@ public void addIntermediateInput(int positionOffset, IntVector groupIds, Page pa for (int i = offset; i < Math.min(groupIds.getPositionCount(), offset + emitChunkSize); i++) { chunk[count++] = groupIds.getInt(i); } - delegate.addIntermediateInput(positionOffset + offset, new IntArrayVector(chunk, count), page); + BlockFactory blockFactory = driverContext().blockFactory(); // TODO: just for compile + delegate.addIntermediateInput(positionOffset + offset, blockFactory.newIntArrayVector(chunk, count), page); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java index 552b0d2d8836f..e6fccf2d46f61 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java @@ -10,7 +10,7 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.CannedSourceOperator; @@ -50,10 +50,11 @@ protected void assertSimpleOutput(List input, Block result) { public void testRejectsDouble() { DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); try ( Driver d = new Driver( driverContext, - new CannedSourceOperator(Iterators.single(new Page(new DoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), + new CannedSourceOperator(Iterators.single(new Page(blockFactory.newDoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), List.of(simple(nonBreakingBigArrays()).get(driverContext)), new PageConsumerOperator(page -> fail("shouldn't have made it this far")), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java index 21880eb6b1a3e..ae5aaa5b21965 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java @@ -10,7 +10,7 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.CannedSourceOperator; @@ -66,10 +66,11 @@ public void testOverflowFails() { public void testRejectsDouble() { DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); try ( Driver d = new Driver( driverContext, - new CannedSourceOperator(Iterators.single(new Page(new DoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), + new CannedSourceOperator(Iterators.single(new Page(blockFactory.newDoubleArrayVector(new double[] { 1.0 }, 1).asBlock()))), List.of(simple(nonBreakingBigArrays()).get(driverContext)), new PageConsumerOperator(page -> fail("shouldn't have made it this far")), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java index f00c8d6d3cb1f..5775a983a2373 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java @@ -10,6 +10,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.BasicBlockTests; @@ -35,6 +36,7 @@ //@TestLogging(value = "org.elasticsearch.compute:TRACE", reason = "debug") public class BlockHashRandomizedTests extends ESTestCase { + @ParametersFactory public static List params() { List params = new ArrayList<>(); @@ -165,7 +167,7 @@ private BlockHash newBlockHash(int emitBatchSize, List types) { for (int c = 0; c < types.size(); c++) { specs.add(new HashAggregationOperator.GroupSpec(c, types.get(c))); } - MockBigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()); return forcePackedHash ? new PackedValuesBlockHash(specs, bigArrays, emitBatchSize) : BlockHash.build(specs, bigArrays, emitBatchSize, true); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java index 553ce83d8002c..620bb5ab5319a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java @@ -10,25 +10,27 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.inject.name.Named; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.HashAggregationOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; +import org.junit.After; import java.util.ArrayList; import java.util.Arrays; @@ -46,6 +48,10 @@ import static org.hamcrest.Matchers.startsWith; public class BlockHashTests extends ESTestCase { + + static final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + static final BlockFactory blockFactory = BlockFactory.getInstance(breaker, BigArrays.NON_RECYCLING_INSTANCE); + @ParametersFactory public static List params() { List params = new ArrayList<>(); @@ -54,6 +60,11 @@ public static List params() { return params; } + @After + public void checkBreaker() { + // assertThat(breaker.getUsed(), is(0L)); // TODO: enable once all blocks are released + } + private final boolean forcePackedHash; public BlockHashTests(@Named("forcePackedHash") boolean forcePackedHash) { @@ -62,7 +73,7 @@ public BlockHashTests(@Named("forcePackedHash") boolean forcePackedHash) { public void testIntHash() { int[] values = new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3 }; - IntBlock block = new IntArrayVector(values, values.length).asBlock(); + IntBlock block = blockFactory.newIntArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -75,6 +86,7 @@ public void testIntHash() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 4))); } assertKeys(ordsAndKeys.keys, 1, 2, 3); + Releasables.closeExpectNoException(block); } public void testIntHashWithNulls() { @@ -150,7 +162,7 @@ public void testIntHashWithMultiValuedFields() { public void testLongHash() { long[] values = new long[] { 2, 1, 4, 2, 4, 1, 3, 4 }; - LongBlock block = new LongArrayVector(values, values.length).asBlock(); + LongBlock block = blockFactory.newLongArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -163,6 +175,7 @@ public void testLongHash() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 5))); } assertKeys(ordsAndKeys.keys, 2L, 1L, 4L, 3L); + Releasables.closeExpectNoException(block); } public void testLongHashWithNulls() { @@ -207,7 +220,8 @@ public void testLongHashWithMultiValuedFields() { builder.appendLong(1); builder.endPositionEntry(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=4, size=")); assertOrds( @@ -234,11 +248,12 @@ public void testLongHashWithMultiValuedFields() { assertKeys(ordsAndKeys.keys, null, 1L, 2L, 3L); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 4))); + Releasables.closeExpectNoException(block); } public void testDoubleHash() { double[] values = new double[] { 2.0, 1.0, 4.0, 2.0, 4.0, 1.0, 3.0, 4.0 }; - DoubleBlock block = new DoubleArrayVector(values, values.length).asBlock(); + DoubleBlock block = blockFactory.newDoubleArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -251,6 +266,7 @@ public void testDoubleHash() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 5))); } assertKeys(ordsAndKeys.keys, 2.0, 1.0, 4.0, 3.0); + Releasables.closeExpectNoException(block); } public void testDoubleHashWithNulls() { @@ -260,7 +276,8 @@ public void testDoubleHashWithNulls() { builder.appendDouble(2); builder.appendNull(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=3, size=")); assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); @@ -271,6 +288,7 @@ public void testDoubleHashWithNulls() { assertKeys(ordsAndKeys.keys, null, 0.0, 2.0); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 3))); + Releasables.closeExpectNoException(block); } public void testDoubleHashWithMultiValuedFields() { @@ -294,7 +312,8 @@ public void testDoubleHashWithMultiValuedFields() { builder.appendDouble(2); builder.endPositionEntry(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=4, size=")); assertOrds( @@ -321,10 +340,11 @@ public void testDoubleHashWithMultiValuedFields() { assertKeys(ordsAndKeys.keys, null, 1.0, 2.0, 3.0); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 4))); + Releasables.closeExpectNoException(block); } public void testBasicBytesRefHash() { - var builder = BytesRefBlock.newBlockBuilder(8); + var builder = blockFactory.newBytesRefBlockBuilder(8); builder.appendBytesRef(new BytesRef("item-2")); builder.appendBytesRef(new BytesRef("item-1")); builder.appendBytesRef(new BytesRef("item-4")); @@ -334,7 +354,8 @@ public void testBasicBytesRefHash() { builder.appendBytesRef(new BytesRef("item-3")); builder.appendBytesRef(new BytesRef("item-4")); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); assertThat(ordsAndKeys.description, endsWith("b}")); @@ -347,6 +368,7 @@ public void testBasicBytesRefHash() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 5))); } assertKeys(ordsAndKeys.keys, "item-2", "item-1", "item-4", "item-3"); + Releasables.closeExpectNoException(block); } public void testBytesRefHashWithNulls() { @@ -356,7 +378,8 @@ public void testBytesRefHashWithNulls() { builder.appendBytesRef(new BytesRef("dog")); builder.appendNull(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=3, size=")); assertThat(ordsAndKeys.description, endsWith("b}")); @@ -369,6 +392,7 @@ public void testBytesRefHashWithNulls() { assertKeys(ordsAndKeys.keys, null, "cat", "dog"); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 3))); + Releasables.closeExpectNoException(block); } public void testBytesRefHashWithMultiValuedFields() { @@ -393,7 +417,8 @@ public void testBytesRefHashWithMultiValuedFields() { builder.appendBytesRef(new BytesRef("bar")); builder.endPositionEntry(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); assertThat(ordsAndKeys.description, endsWith("b}")); @@ -422,11 +447,12 @@ public void testBytesRefHashWithMultiValuedFields() { assertKeys(ordsAndKeys.keys, null, "foo", "bar", "bort"); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 4))); + Releasables.closeExpectNoException(block); } public void testBooleanHashFalseFirst() { boolean[] values = new boolean[] { false, true, true, true, true }; - BooleanBlock block = new BooleanArrayVector(values, values.length).asBlock(); + BooleanBlock block = blockFactory.newBooleanArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -439,11 +465,12 @@ public void testBooleanHashFalseFirst() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 3))); } assertKeys(ordsAndKeys.keys, false, true); + Releasables.closeExpectNoException(block); } public void testBooleanHashTrueFirst() { boolean[] values = new boolean[] { true, false, false, true, true }; - BooleanBlock block = new BooleanArrayVector(values, values.length).asBlock(); + BooleanBlock block = blockFactory.newBooleanArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -457,11 +484,12 @@ public void testBooleanHashTrueFirst() { assertKeys(ordsAndKeys.keys, false, true); assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(1, 3))); } + Releasables.closeExpectNoException(block); } public void testBooleanHashTrueOnly() { boolean[] values = new boolean[] { true, true, true, true }; - BooleanBlock block = new BooleanArrayVector(values, values.length).asBlock(); + BooleanBlock block = blockFactory.newBooleanArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -475,11 +503,12 @@ public void testBooleanHashTrueOnly() { assertKeys(ordsAndKeys.keys, true); assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(2).build())); } + Releasables.closeExpectNoException(block); } public void testBooleanHashFalseOnly() { boolean[] values = new boolean[] { false, false, false, false }; - BooleanBlock block = new BooleanArrayVector(values, values.length).asBlock(); + BooleanBlock block = blockFactory.newBooleanArrayVector(values, values.length).asBlock(); OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { @@ -492,6 +521,7 @@ public void testBooleanHashFalseOnly() { assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(1).build())); } assertKeys(ordsAndKeys.keys, false); + Releasables.closeExpectNoException(block); } public void testBooleanHashWithNulls() { @@ -501,7 +531,8 @@ public void testBooleanHashWithNulls() { builder.appendBoolean(true); builder.appendNull(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); @@ -512,6 +543,7 @@ public void testBooleanHashWithNulls() { assertKeys(ordsAndKeys.keys, null, false, true); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 3))); + Releasables.closeExpectNoException(block); } public void testBooleanHashWithMultiValuedFields() { @@ -535,7 +567,8 @@ public void testBooleanHashWithMultiValuedFields() { builder.appendBoolean(false); builder.endPositionEntry(); - OrdsAndKeys ordsAndKeys = hash(builder.build()); + Block block = builder.build(); + OrdsAndKeys ordsAndKeys = hash(block); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); assertOrds( @@ -562,13 +595,14 @@ public void testBooleanHashWithMultiValuedFields() { assertKeys(ordsAndKeys.keys, null, false, true); } assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 3))); + Releasables.closeExpectNoException(block); } public void testLongLongHash() { long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = new LongArrayVector(values1, values1.length).asBlock(); + LongBlock block1 = blockFactory.newLongArrayVector(values1, values1.length).asBlock(); long[] values2 = new long[] { 0, 0, 0, 1, 1, 1 }; - LongBlock block2 = new LongArrayVector(values2, values2.length).asBlock(); + LongBlock block2 = blockFactory.newLongArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0L, 0L }, new Object[] { 1L, 0L }, new Object[] { 1L, 1L }, new Object[] { 0L, 1L } }; OrdsAndKeys ordsAndKeys = hash(block1, block2); @@ -581,6 +615,7 @@ public void testLongLongHash() { assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); assertKeys(ordsAndKeys.keys, expectedKeys); assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 4))); + Releasables.closeExpectNoException(block1, block2); } private void append(LongBlock.Builder b1, LongBlock.Builder b2, long[] v1, long[] v2) { @@ -621,7 +656,9 @@ public void testLongLongHashWithMultiValuedFields() { append(b1, b2, new long[] { 1, 1, 2, 2 }, new long[] { 10, 20, 20 }); append(b1, b2, new long[] { 1, 2, 3 }, new long[] { 30, 30, 10 }); - OrdsAndKeys ordsAndKeys = hash(b1.build(), b2.build()); + Block block1 = b1.build(); + Block block2 = b2.build(); + OrdsAndKeys ordsAndKeys = hash(block1, block2); if (forcePackedHash) { assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=10, size=")); assertOrds( @@ -678,6 +715,7 @@ public void testLongLongHashWithMultiValuedFields() { new Object[] { 3L, 10L }, } ); assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 8))); + Releasables.closeExpectNoException(block1, block2); } } @@ -715,9 +753,9 @@ public void testLongLongHashHugeCombinatorialExplosion() { public void testIntLongHash() { int[] values1 = new int[] { 0, 1, 0, 1, 0, 1 }; - IntBlock block1 = new IntArrayVector(values1, values1.length).asBlock(); + IntBlock block1 = blockFactory.newIntArrayVector(values1, values1.length).asBlock(); long[] values2 = new long[] { 0, 0, 0, 1, 1, 1 }; - LongBlock block2 = new LongArrayVector(values2, values2.length).asBlock(); + LongBlock block2 = blockFactory.newLongArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0, 0L }, new Object[] { 1, 0L }, new Object[] { 1, 1L }, new Object[] { 0, 1L } }; OrdsAndKeys ordsAndKeys = hash(block1, block2); @@ -725,13 +763,14 @@ public void testIntLongHash() { assertThat(ordsAndKeys.description, endsWith("b}")); assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); assertKeys(ordsAndKeys.keys, expectedKeys); + Releasables.closeExpectNoException(block1, block2); } public void testLongDoubleHash() { long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = new LongArrayVector(values1, values1.length).asBlock(); + LongBlock block1 = blockFactory.newLongArrayVector(values1, values1.length).asBlock(); double[] values2 = new double[] { 0, 0, 0, 1, 1, 1 }; - DoubleBlock block2 = new DoubleArrayVector(values2, values2.length).asBlock(); + DoubleBlock block2 = blockFactory.newDoubleArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0L, 0d }, new Object[] { 1L, 0d }, new Object[] { 1L, 1d }, new Object[] { 0L, 1d } }; OrdsAndKeys ordsAndKeys = hash(block1, block2); assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:DOUBLE], entries=4, size=")); @@ -742,9 +781,9 @@ public void testLongDoubleHash() { public void testIntBooleanHash() { int[] values1 = new int[] { 0, 1, 0, 1, 0, 1 }; - IntBlock block1 = new IntArrayVector(values1, values1.length).asBlock(); + IntBlock block1 = blockFactory.newIntArrayVector(values1, values1.length).asBlock(); boolean[] values2 = new boolean[] { false, false, false, true, true, true }; - BooleanBlock block2 = new BooleanArrayVector(values2, values2.length).asBlock(); + BooleanBlock block2 = blockFactory.newBooleanArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0, false }, new Object[] { 1, false }, @@ -796,8 +835,8 @@ public void testLongLongHashWithNull() { public void testLongBytesRefHash() { long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = new LongArrayVector(values1, values1.length).asBlock(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(8); + LongBlock block1 = blockFactory.newLongArrayVector(values1, values1.length).asBlock(); + BytesRefBlock.Builder builder = blockFactory.newBytesRefBlockBuilder(8); builder.appendBytesRef(new BytesRef("cat")); builder.appendBytesRef(new BytesRef("cat")); builder.appendBytesRef(new BytesRef("cat")); @@ -824,6 +863,7 @@ public void testLongBytesRefHash() { assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); assertKeys(ordsAndKeys.keys, expectedKeys); assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.range(0, 4))); + Releasables.closeExpectNoException(block1, block2); } public void testLongBytesRefHashWithNull() { @@ -1028,7 +1068,7 @@ private void hash(Consumer callback, int emitBatchSize, Block... va for (int c = 0; c < values.length; c++) { specs.add(new HashAggregationOperator.GroupSpec(c, values[c].elementType())); } - MockBigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()); try ( BlockHash blockHash = forcePackedHash ? new PackedValuesBlockHash(specs, bigArrays, emitBatchSize) @@ -1068,6 +1108,7 @@ public void add(int positionOffset, IntBlock groupIds) { } } callback.accept(result); + // Releasables.closeExpectNoException(result.keys()); // TODO: who should release the keys? } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index b3c0624496bde..df16f0036c767 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -7,10 +7,20 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.Accountable; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.test.ESTestCase; +import org.junit.After; +import org.junit.Before; import java.util.ArrayList; import java.util.Arrays; @@ -26,51 +36,48 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class BasicBlockTests extends ESTestCase { - public void testEmpty() { - assertThat( - new IntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(IntBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new IntArrayVector(new int[] {}, 0).getPositionCount(), is(0)); - assertThat(IntVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + final BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); - assertThat( - new LongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(LongBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new LongArrayVector(new long[] {}, 0).getPositionCount(), is(0)); - assertThat(LongVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + @Before + @After + public void checkBreaker() { + assertThat(breaker.getUsed(), is(0L)); + } - assertThat( - new DoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(DoubleBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new DoubleArrayVector(new double[] {}, 0).getPositionCount(), is(0)); - assertThat(DoubleVector.newVectorBuilder(0).build().getPositionCount(), is(0)); - - var emptyArray = new BytesRefArray(0, BigArrays.NON_RECYCLING_INSTANCE); - assertThat( - new BytesRefArrayBlock(emptyArray, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(BytesRefBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new BytesRefArrayVector(emptyArray, 0).getPositionCount(), is(0)); - assertThat(BytesRefVector.newVectorBuilder(0).build().getPositionCount(), is(0)); - - assertThat( - new BooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())) - .getPositionCount(), - is(0) + public void testEmpty() { + testEmpty(blockFactory); + } + + void testEmpty(BlockFactory bf) { + assertZeroPositionsAndRelease(bf.newIntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(IntBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newIntArrayVector(new int[] {}, 0)); + assertZeroPositionsAndRelease(IntVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newLongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(LongBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newLongArrayVector(new long[] {}, 0)); + assertZeroPositionsAndRelease(LongVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newDoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(DoubleBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newDoubleArrayVector(new double[] {}, 0)); + assertZeroPositionsAndRelease(DoubleVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease( + bf.newBytesRefArrayBlock(new BytesRefArray(0, bf.bigArrays()), 0, new int[] {}, new BitSet(), randomOrdering()) ); - assertThat(BooleanBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new BooleanArrayVector(new boolean[] {}, 0).getPositionCount(), is(0)); - assertThat(BooleanVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + assertZeroPositionsAndRelease(BytesRefBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBytesRefArrayVector(new BytesRefArray(0, bf.bigArrays()), 0)); + assertZeroPositionsAndRelease(BytesRefVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(BooleanBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBooleanArrayVector(new boolean[] {}, 0)); + assertZeroPositionsAndRelease(BooleanVector.newVectorBuilder(0, bf).build()); } public void testSmallSingleValueDenseGrowthInt() { @@ -141,15 +148,16 @@ static void assertSingleValueDenseBlock(Block initialBlock) { public void testIntBlock() { for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); IntBlock block; if (randomBoolean()) { final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(builderEstimateSize); + IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(builderEstimateSize, blockFactory); IntStream.range(0, positionCount).forEach(blockBuilder::appendInt); block = blockBuilder.build(); } else { - block = new IntArrayVector(IntStream.range(0, positionCount).toArray(), positionCount).asBlock(); + block = blockFactory.newIntArrayVector(IntStream.range(0, positionCount).toArray(), positionCount).asBlock(); } assertThat(block.getPositionCount(), equalTo(positionCount)); @@ -158,11 +166,12 @@ public void testIntBlock() { int pos = block.getInt(randomPosition(positionCount)); assertThat(pos, is(block.getInt(pos))); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); if (positionCount > 1) { assertNullValues( positionCount, - size -> IntBlock.newBlockBuilder(size), + size -> IntBlock.newBlockBuilder(size, blockFactory), (bb, value) -> bb.appendInt(value), position -> position, IntBlock.Builder::build, @@ -172,28 +181,32 @@ public void testIntBlock() { ); } - IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(1); + IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(1, blockFactory); IntBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(copy); IntVector.Builder vectorBuilder = IntVector.newVectorBuilder( - randomBoolean() ? randomIntBetween(1, positionCount) : positionCount + randomBoolean() ? randomIntBetween(1, positionCount) : positionCount, + blockFactory ); IntStream.range(0, positionCount).forEach(vectorBuilder::appendInt); IntVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); + releaseAndAssertBreaker(vector); } } public void testConstantIntBlock() { for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); int value = randomInt(); IntBlock block; if (randomBoolean()) { - block = IntBlock.newConstantBlockWith(value, positionCount); + block = IntBlock.newConstantBlockWith(value, positionCount, blockFactory); } else { - block = new ConstantIntVector(value, positionCount).asBlock(); + block = blockFactory.newConstantIntBlockWith(value, positionCount); } assertThat(positionCount, is(block.getPositionCount())); assertThat(value, is(block.getInt(0))); @@ -201,20 +214,22 @@ public void testConstantIntBlock() { assertThat(value, is(block.getInt(randomPosition(positionCount)))); assertThat(block.isNull(randomPosition(positionCount)), is(false)); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); } } public void testLongBlock() { for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); LongBlock block; if (randomBoolean()) { final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - LongBlock.Builder blockBuilder = LongBlock.newBlockBuilder(builderEstimateSize); + LongBlock.Builder blockBuilder = blockFactory.newLongBlockBuilder(builderEstimateSize); LongStream.range(0, positionCount).forEach(blockBuilder::appendLong); block = blockBuilder.build(); } else { - block = new LongArrayVector(LongStream.range(0, positionCount).toArray(), positionCount).asBlock(); + block = blockFactory.newLongArrayVector(LongStream.range(0, positionCount).toArray(), positionCount).asBlock(); } assertThat(positionCount, is(block.getPositionCount())); @@ -223,11 +238,12 @@ public void testLongBlock() { int pos = (int) block.getLong(randomPosition(positionCount)); assertThat((long) pos, is(block.getLong(pos))); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); if (positionCount > 1) { assertNullValues( positionCount, - size -> LongBlock.newBlockBuilder(size), + size -> LongBlock.newBlockBuilder(size, blockFactory), (bb, value) -> bb.appendLong(value), position -> (long) position, LongBlock.Builder::build, @@ -252,13 +268,14 @@ public void testLongBlock() { public void testConstantLongBlock() { for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); long value = randomLong(); LongBlock block; if (randomBoolean()) { - block = LongBlock.newConstantBlockWith(value, positionCount); + block = LongBlock.newConstantBlockWith(value, positionCount, blockFactory); } else { - block = new ConstantLongVector(value, positionCount).asBlock(); + block = blockFactory.newConstantLongBlockWith(value, positionCount); } assertThat(positionCount, is(block.getPositionCount())); assertThat(value, is(block.getLong(0))); @@ -266,9 +283,14 @@ public void testConstantLongBlock() { assertThat(value, is(block.getLong(randomPosition(positionCount)))); assertThat(block.isNull(randomPosition(positionCount)), is(false)); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); } } + // TODO: continue to update the test, as above. + // Try to not complicate the "basic" test any more than necessary, but it already has great coverage + // for building all types of blocks!! + public void testDoubleBlock() { for (int i = 0; i < 1000; i++) { int positionCount = randomIntBetween(1, 16 * 1024); @@ -292,7 +314,7 @@ public void testDoubleBlock() { if (positionCount > 1) { assertNullValues( positionCount, - size -> DoubleBlock.newBlockBuilder(size), + size -> DoubleBlock.newBlockBuilder(size, blockFactory), (bb, value) -> bb.appendDouble(value), position -> (double) position, DoubleBlock.Builder::build, @@ -369,7 +391,7 @@ public void testBytesRefBlock() { if (positionCount > 1) { assertNullValues( positionCount, - size -> BytesRefBlock.newBlockBuilder(size), + size -> BytesRefBlock.newBlockBuilder(size, blockFactory), (bb, value) -> bb.appendBytesRef(value), position -> values[position], BytesRefBlock.Builder::build, @@ -479,7 +501,7 @@ public void testBooleanBlock() { if (positionCount > 1) { assertNullValues( positionCount, - BooleanBlock::newBlockBuilder, + size -> BooleanBlock.newBlockBuilder(size, blockFactory), (bb, value) -> bb.appendBoolean(value), position -> position % 10 == 0, BooleanBlock.Builder::build, @@ -520,6 +542,18 @@ public void testConstantBooleanBlock() { } } + public void testConstantNullBlock() { + for (int i = 0; i < 100; i++) { + assertThat(breaker.getUsed(), is(0L)); + int positionCount = randomIntBetween(1, 16 * 1024); + Block block = Block.constantNullBlock(positionCount, blockFactory); + assertThat(positionCount, is(block.getPositionCount())); + assertThat(block.getPositionCount(), is(positionCount)); + assertThat(block.isNull(randomPosition(positionCount)), is(true)); + releaseAndAssertBreaker(block); + } + } + public void testSingleValueSparseInt() { int positionCount = randomIntBetween(2, 16 * 1024); final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; @@ -851,9 +885,41 @@ private static void assertNullVal asserter.accept(randomNonNullPosition, block); assertTrue(block.isNull(randomNullPosition)); assertFalse(block.isNull(randomNonNullPosition)); + releaseAndAssertBreaker(block, block.blockFactory().breaker()); + } + + void assertZeroPositionsAndRelease(Block block) { + assertThat(block.getPositionCount(), is(0)); + releaseAndAssertBreaker(block); + } + + void assertZeroPositionsAndRelease(Vector vector) { + assertThat(vector.getPositionCount(), is(0)); + releaseAndAssertBreaker(vector); + } + + void releaseAndAssertBreaker(T data) { + releaseAndAssertBreaker(data, breaker); + } + + static void releaseAndAssertBreaker(T data, CircuitBreaker breaker) { + assertThat(breaker.getUsed(), greaterThan(0L)); + Releasables.closeExpectNoException(data); + assertThat(breaker.getUsed(), is(0L)); } static int randomPosition(int positionCount) { return positionCount == 1 ? 0 : randomIntBetween(0, positionCount - 1); } + + static Block.MvOrdering randomOrdering() { + return randomFrom(Block.MvOrdering.values()); + } + + // A breaker service that always returns the given breaker for getBreaker(CircuitBreaker.REQUEST) + static CircuitBreakerService mockBreakerService(CircuitBreaker breaker) { + CircuitBreakerService breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return breakerService; + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockAccountingTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockAccountingTests.java index 5503c02be9794..c93b07e4d40f3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockAccountingTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockAccountingTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.data; import org.apache.lucene.tests.util.RamUsageTester; +import org.apache.lucene.tests.util.RamUsageTester.Accumulator; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArray; import org.elasticsearch.common.util.BigArrays; @@ -28,13 +29,15 @@ public class BlockAccountingTests extends ESTestCase { + static final Accumulator RAM_USAGE_ACCUMULATOR = new TestRamUsageAccumulator(); + // A large(ish) upperbound simply so that effective greaterThan assertions are not unbounded static final long UPPER_BOUND = 10_000; // Array Vectors public void testBooleanVector() { Vector empty = new BooleanArrayVector(new boolean[] {}, 0); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Vector emptyPlusOne = new BooleanArrayVector(new boolean[] { randomBoolean() }, 1); @@ -51,7 +54,7 @@ public void testBooleanVector() { public void testIntVector() { Vector empty = new IntArrayVector(new int[] {}, 0); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Vector emptyPlusOne = new IntArrayVector(new int[] { randomInt() }, 1); @@ -68,7 +71,7 @@ public void testIntVector() { public void testLongVector() { Vector empty = new LongArrayVector(new long[] {}, 0); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Vector emptyPlusOne = new LongArrayVector(new long[] { randomLong() }, 1); @@ -85,7 +88,7 @@ public void testLongVector() { public void testDoubleVector() { Vector empty = new DoubleArrayVector(new double[] {}, 0); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Vector emptyPlusOne = new DoubleArrayVector(new double[] { randomDouble() }, 1); @@ -105,23 +108,8 @@ public void testBytesRefVector() { var emptyArray = new BytesRefArray(0, BigArrays.NON_RECYCLING_INSTANCE); var arrayWithOne = new BytesRefArray(0, BigArrays.NON_RECYCLING_INSTANCE) ) { - var acc = new RamUsageTester.Accumulator() { - @Override - public long accumulateObject(Object o, long shallowSize, Map fieldValues, Collection queue) { - for (var entry : fieldValues.entrySet()) { - if (entry.getKey().getType().equals(BigArrays.class)) { - // skip BigArrays, as it is (correctly) not part of the ramBytesUsed for BytesRefArray - } else if (o instanceof BigArray bigArray) { - return bigArray.ramBytesUsed(); - } else { - queue.add(entry.getValue()); - } - } - return shallowSize; - } - }; Vector emptyVector = new BytesRefArrayVector(emptyArray, 0); - long expectedEmptyVectorUsed = RamUsageTester.ramUsed(emptyVector, acc); + long expectedEmptyVectorUsed = RamUsageTester.ramUsed(emptyVector, RAM_USAGE_ACCUMULATOR); assertThat(emptyVector.ramBytesUsed(), is(expectedEmptyVectorUsed)); var bytesRef = new BytesRef(randomAlphaOfLengthBetween(1, 16)); @@ -138,7 +126,7 @@ public long accumulateObject(Object o, long shallowSize, Map fiel // Array Blocks public void testBooleanBlock() { Block empty = new BooleanArrayBlock(new boolean[] {}, 0, new int[] {}, null, Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Block emptyPlusOne = new BooleanArrayBlock(new boolean[] { randomBoolean() }, 1, new int[] {}, null, Block.MvOrdering.UNORDERED); @@ -154,13 +142,13 @@ public void testBooleanBlock() { public void testBooleanBlockWithNullFirstValues() { Block empty = new BooleanArrayBlock(new boolean[] {}, 0, null, BitSet.valueOf(new byte[] { 1 }), Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), lessThanOrEqualTo(expectedEmptyUsed)); } public void testIntBlock() { Block empty = new IntArrayBlock(new int[] {}, 0, new int[] {}, null, Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Block emptyPlusOne = new IntArrayBlock(new int[] { randomInt() }, 1, new int[] {}, null, Block.MvOrdering.UNORDERED); @@ -176,13 +164,13 @@ public void testIntBlock() { public void testIntBlockWithNullFirstValues() { Block empty = new IntArrayBlock(new int[] {}, 0, null, BitSet.valueOf(new byte[] { 1 }), Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); } public void testLongBlock() { Block empty = new LongArrayBlock(new long[] {}, 0, new int[] {}, null, Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Block emptyPlusOne = new LongArrayBlock(new long[] { randomInt() }, 1, new int[] {}, null, Block.MvOrdering.UNORDERED); @@ -198,13 +186,13 @@ public void testLongBlock() { public void testLongBlockWithNullFirstValues() { Block empty = new LongArrayBlock(new long[] {}, 0, null, BitSet.valueOf(new byte[] { 1 }), Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); } public void testDoubleBlock() { Block empty = new DoubleArrayBlock(new double[] {}, 0, new int[] {}, null, Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); Block emptyPlusOne = new DoubleArrayBlock(new double[] { randomInt() }, 1, new int[] {}, null, Block.MvOrdering.UNORDERED); @@ -220,11 +208,28 @@ public void testDoubleBlock() { public void testDoubleBlockWithNullFirstValues() { Block empty = new DoubleArrayBlock(new double[] {}, 0, null, BitSet.valueOf(new byte[] { 1 }), Block.MvOrdering.UNORDERED); - long expectedEmptyUsed = RamUsageTester.ramUsed(empty); + long expectedEmptyUsed = RamUsageTester.ramUsed(empty, RAM_USAGE_ACCUMULATOR); assertThat(empty.ramBytesUsed(), is(expectedEmptyUsed)); } static Matcher between(long minInclusive, long maxInclusive) { return allOf(greaterThanOrEqualTo(minInclusive), lessThanOrEqualTo(maxInclusive)); } + + /** An accumulator that stops at BigArrays or BlockFactory. And calls ramBytesUsed on BigArray instances. */ + static class TestRamUsageAccumulator extends Accumulator { + @Override + public long accumulateObject(Object o, long shallowSize, Map fieldValues, Collection queue) { + for (var entry : fieldValues.entrySet()) { + if (entry.getKey().getType().equals(BigArrays.class) || entry.getKey().getType().equals(BlockFactory.class)) { + // skip BigArrays, as it is (correctly) not part of the ramBytesUsed for BytesRefArray + } else if (o instanceof BigArray bigArray) { + return bigArray.ramBytesUsed(); + } else { + queue.add(entry.getValue()); + } + } + return shallowSize; + } + }; } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderAppendBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderAppendBlockTests.java index 1feabec374170..a9f08eee02d70 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderAppendBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderAppendBlockTests.java @@ -17,7 +17,7 @@ public class BlockBuilderAppendBlockTests extends ESTestCase { public void testBasic() { - IntBlock src = new IntBlockBuilder(10).appendInt(1) + IntBlock src = new IntBlockBuilder(10, BlockFactory.getNonBreakingInstance()).appendInt(1) .appendNull() .beginPositionEntry() .appendInt(4) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java new file mode 100644 index 0000000000000..831be03cb0c81 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -0,0 +1,564 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.Accountable; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.elasticsearch.test.ESTestCase; +import org.junit.After; +import org.junit.Before; + +import java.util.BitSet; +import java.util.List; +import java.util.function.Supplier; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +// BlockFactory is used and effectively tested in many other places, but this class contains tests +// more specific to the factory implementation itself (and not necessarily tested elsewhere). +public class BlockFactoryTests extends ESTestCase { + + final CircuitBreaker breaker; + final BigArrays bigArrays; + final BlockFactory blockFactory; + + @ParametersFactory + public static List params() { + List> l = List.of(() -> { + CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + return BlockFactory.getInstance(breaker, bigArrays); + }, BlockFactory::getGlobalInstance); + return l.stream().map(s -> new Object[] { s }).toList(); + } + + public BlockFactoryTests(@Name("blockFactorySupplier") Supplier blockFactorySupplier) { + this.blockFactory = blockFactorySupplier.get(); + this.breaker = blockFactory.breaker(); + this.bigArrays = blockFactory.bigArrays(); + } + + @Before + @After + public void checkBreaker() { + assertThat(breaker.getUsed(), is(0L)); + } + + public void testPreAdjusters() { + for (int i = 0; i < 1000; i++) { + int positions = randomIntBetween(1, 16384); + long preAdjustBytes = blockFactory.preAdjustBreakerForBoolean(positions); + assertThat(preAdjustBytes, is((long) positions)); + blockFactory.adjustBreaker(-preAdjustBytes, true); + + preAdjustBytes = blockFactory.preAdjustBreakerForInt(positions); + assertThat(preAdjustBytes, is((long) positions * 4)); + blockFactory.adjustBreaker(-preAdjustBytes, true); + + preAdjustBytes = blockFactory.preAdjustBreakerForLong(positions); + assertThat(preAdjustBytes, is((long) positions * 8)); + blockFactory.adjustBreaker(-preAdjustBytes, true); + + preAdjustBytes = blockFactory.preAdjustBreakerForDouble(positions); + assertThat(preAdjustBytes, is((long) positions * 8)); + blockFactory.adjustBreaker(-preAdjustBytes, true); + } + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newIntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newIntArrayBlock(new int[] { randomInt() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantIntBlockWith(randomInt(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendInt(randomInt()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendInt(randomInt()); + builder.appendInt(randomInt()); + builder.endPositionEntry(); + } + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newIntArrayVector(new int[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newIntArrayVector(new int[] { randomInt() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantIntBlockWith(randomInt(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + if (randomBoolean()) { // constant-ness or not + builder.appendInt(randomInt()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newLongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newLongArrayBlock(new long[] { randomLong() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantLongBlockWith(randomLong(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendLong(randomLong()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendLong(randomInt()); + builder.appendLong(randomInt()); + builder.endPositionEntry(); + } + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newLongArrayVector(new long[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newLongArrayVector(new long[] { randomLong() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantLongBlockWith(randomLong(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + if (randomBoolean()) { // constant-ness or not + builder.appendLong(randomLong()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newDoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newDoubleArrayBlock(new double[] { randomDouble() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantDoubleBlockWith(randomDouble(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendDouble(randomDouble()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendDouble(randomDouble()); + builder.appendDouble(randomDouble()); + builder.endPositionEntry(); + } + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newDoubleArrayVector(new double[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newDoubleArrayVector(new double[] { randomDouble() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantDoubleBlockWith(randomDouble(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + if (randomBoolean()) { // constant-ness or not + builder.appendDouble(randomDouble()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newBooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newBooleanArrayBlock(new boolean[] { randomBoolean() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantBooleanBlockWith(randomBoolean(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendBoolean(randomBoolean()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendBoolean(randomBoolean()); + builder.appendBoolean(randomBoolean()); + builder.endPositionEntry(); + } + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newBooleanArrayVector(new boolean[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newBooleanArrayVector(new boolean[] { randomBoolean() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantBooleanBlockWith(randomBoolean(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + if (randomBoolean()) { // constant-ness or not + builder.appendBoolean(randomBoolean()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + var emptyArray = new BytesRefArray(0, bigArrays); + block = blockFactory.newBytesRefArrayBlock(emptyArray, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + var array = new BytesRefArray(1, bigArrays); + array.append(randomBytesRef()); + block = blockFactory.newBytesRefArrayBlock(array, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantBytesRefBlockWith(randomBytesRef(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendBytesRef(randomBytesRef()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendBytesRef(randomBytesRef()); + builder.appendBytesRef(randomBytesRef()); + builder.endPositionEntry(); + } + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + var emptyArray = new BytesRefArray(0, bigArrays); + vector = blockFactory.newBytesRefArrayVector(emptyArray, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + var array = new BytesRefArray(1, bigArrays); + array.append(randomBytesRef()); + vector = blockFactory.newBytesRefArrayVector(array, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantBytesRefBlockWith(randomBytesRef(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + if (randomBoolean()) { // constant-ness or not + builder.appendBytesRef(randomBytesRef()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + static BytesRef randomBytesRef() { + return new BytesRef(randomByteArrayOfLength(between(1, 20))); + } + + static Block.MvOrdering randomOrdering() { + return randomFrom(Block.MvOrdering.values()); + } + + void releaseAndAssertBreaker(T data) { + assertThat(breaker.getUsed(), greaterThan(0L)); + Releasables.closeExpectNoException(data); + assertThat(breaker.getUsed(), is(0L)); + } + + // A breaker service that always returns the given breaker for getBreaker(CircuitBreaker.REQUEST) + static CircuitBreakerService mockBreakerService(CircuitBreaker breaker) { + CircuitBreakerService breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return breakerService; + } +} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java index 7bfeb57e6999a..edbc59f9497fc 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; @@ -95,6 +96,6 @@ protected final BigArrays nonBreakingBigArrays() { * A {@link DriverContext} with a nonBreakingBigArrays. */ protected final DriverContext driverContext() { - return new DriverContext(nonBreakingBigArrays()); + return new DriverContext(nonBreakingBigArrays(), BlockFactory.getNonBreakingInstance()); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AsyncOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AsyncOperatorTests.java index 7365a55778084..50d41978aa84f 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AsyncOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AsyncOperatorTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; @@ -214,7 +215,8 @@ protected void doRun() { */ DriverContext driverContext() { return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getNonBreakingInstance() ); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverContextTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverContextTests.java index dcf56c09efe05..99d7a0eb01748 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverContextTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverContextTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.core.Releasable; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; @@ -144,7 +145,10 @@ static class AssertingDriverContext extends DriverContext { volatile Thread thread; AssertingDriverContext() { - super(new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService())); + super( + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()), + BlockFactory.getNonBreakingInstance() + ); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java index 8355a5a444bf5..6a2ace060e1e6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import org.elasticsearch.core.TimeValue; import org.elasticsearch.indices.CrankyCircuitBreakerService; @@ -80,9 +81,11 @@ public final void testSimpleLargeInput() { */ public final void testSimpleCircuitBreaking() { BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, smallEnoughToCircuitBreak()); + CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); Exception e = expectThrows( CircuitBreakingException.class, - () -> assertSimple(new DriverContext(bigArrays), between(1_000, 10_000)) + () -> assertSimple(new DriverContext(bigArrays, blockFactory), between(1_000, 10_000)) ); assertThat(e.getMessage(), equalTo(MockBigArrays.ERROR_MESSAGE)); assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); @@ -97,8 +100,9 @@ public final void testSimpleCircuitBreaking() { public final void testSimpleWithCranky() { CrankyCircuitBreakerService breaker = new CrankyCircuitBreakerService(); BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, breaker).withCircuitBreaking(); + BlockFactory blockFactory = BlockFactory.getInstance(breaker.getBreaker("request"), bigArrays); try { - assertSimple(new DriverContext(bigArrays), between(1_000, 10_000)); + assertSimple(new DriverContext(bigArrays, blockFactory), between(1_000, 10_000)); // Either we get lucky and cranky doesn't throw and the test completes or we don't and it throws } catch (CircuitBreakingException e) { assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); @@ -173,7 +177,7 @@ public static void runDriver(List drivers) { drivers.add( new Driver( "dummy-session", - new DriverContext(BigArrays.NON_RECYCLING_INSTANCE), + new DriverContext(BigArrays.NON_RECYCLING_INSTANCE, BlockFactory.getNonBreakingInstance()), () -> "dummy-driver", new SequenceLongBlockSourceOperator(LongStream.range(0, between(1, 100)), between(1, 100)), List.of(), diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/RowOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/RowOperatorTests.java index bb2713e105b93..c8250eba5703a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/RowOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/RowOperatorTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DoubleBlock; @@ -26,7 +27,8 @@ public class RowOperatorTests extends ESTestCase { final DriverContext driverContext = new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getNonBreakingInstance() ); public void testBoolean() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceDoubleBlockSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceDoubleBlockSourceOperator.java index 4c1590ae9b8ff..af14d0be0710c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceDoubleBlockSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceDoubleBlockSourceOperator.java @@ -7,7 +7,7 @@ package org.elasticsearch.compute.operator; -import org.elasticsearch.compute.data.DoubleArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import java.util.List; @@ -48,7 +48,7 @@ protected Page createPage(int positionOffset, int length) { array[i] = values[positionOffset + i]; } currentPosition += length; - return new Page(new DoubleArrayVector(array, array.length).asBlock()); + return new Page(BlockFactory.getNonBreakingInstance().newDoubleArrayVector(array, array.length).asBlock()); } protected int remaining() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java index 8600237401ed0..0aa78f3ad0ab3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java @@ -7,7 +7,7 @@ package org.elasticsearch.compute.operator; -import org.elasticsearch.compute.data.LongArrayVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import java.util.List; @@ -48,7 +48,7 @@ protected Page createPage(int positionOffset, int length) { array[i] = values[positionOffset + i]; } currentPosition += length; - return new Page(new LongArrayVector(array, array.length).asBlock()); + return new Page(BlockFactory.getNonBreakingInstance().newLongArrayVector(array, array.length).asBlock()); // TODO: just for compile } protected int remaining() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java index 87fd1513aaf2d..5b6b33ea0b80a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ConstantIntVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Page; @@ -478,7 +479,8 @@ public void sendResponse(Exception exception) throws IOException { */ DriverContext driverContext() { return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getNonBreakingInstance() ); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java index a3e060f5693d8..7491ffde6766e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DoubleBlock; @@ -422,12 +423,15 @@ public void testTopNTwoColumns() { } public void testCollectAllValues() { + DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); + int size = 10; int topCount = 3; List blocks = new ArrayList<>(); List> expectedTop = new ArrayList<>(); - IntBlock keys = new IntArrayVector(IntStream.range(0, size).toArray(), size).asBlock(); + IntBlock keys = blockFactory.newIntArrayVector(IntStream.range(0, size).toArray(), size).asBlock(); List topKeys = new ArrayList<>(IntStream.range(size - topCount, size).boxed().toList()); Collections.reverse(topKeys); expectedTop.add(topKeys); @@ -461,7 +465,6 @@ public void testCollectAllValues() { } List> actualTop = new ArrayList<>(); - DriverContext driverContext = driverContext(); try ( Driver driver = new Driver( driverContext, @@ -488,13 +491,16 @@ public void testCollectAllValues() { } public void testCollectAllValues_RandomMultiValues() { + DriverContext driverContext = driverContext(); + BlockFactory blockFactory = driverContext.blockFactory(); + int rows = 10; int topCount = 3; int blocksCount = 20; List blocks = new ArrayList<>(); List> expectedTop = new ArrayList<>(); - IntBlock keys = new IntArrayVector(IntStream.range(0, rows).toArray(), rows).asBlock(); + IntBlock keys = blockFactory.newIntArrayVector(IntStream.range(0, rows).toArray(), rows).asBlock(); List topKeys = new ArrayList<>(IntStream.range(rows - topCount, rows).boxed().toList()); Collections.reverse(topKeys); expectedTop.add(topKeys); @@ -546,7 +552,6 @@ public void testCollectAllValues_RandomMultiValues() { expectedTop.add(eTop); } - DriverContext driverContext = driverContext(); List> actualTop = new ArrayList<>(); try ( Driver driver = new Driver( diff --git a/x-pack/plugin/esql/compute/src/test/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters b/x-pack/plugin/esql/compute/src/test/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters new file mode 100644 index 0000000000000..2536d1e189285 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters @@ -0,0 +1,8 @@ +# +# 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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +org.elasticsearch.compute.TestBlockFactoryParameters diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java index 3829ed3ac3198..d6611881f8546 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.LongBlock; @@ -230,7 +231,8 @@ public void testMultipleMatches() { static DriverContext driverContext() { return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getGlobalInstance() ); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index df7058e28fb43..be75c0d1c05d6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.lucene.ValueSources; @@ -98,18 +99,21 @@ public class EnrichLookupService { private final TransportService transportService; private final Executor executor; private final BigArrays bigArrays; + private final BlockFactory blockFactory; public EnrichLookupService( ClusterService clusterService, SearchService searchService, TransportService transportService, - BigArrays bigArrays + BigArrays bigArrays, + BlockFactory blockFactory ) { this.clusterService = clusterService; this.searchService = searchService; this.transportService = transportService; this.executor = transportService.getThreadPool().executor(EsqlPlugin.ESQL_THREAD_POOL_NAME); this.bigArrays = bigArrays; + this.blockFactory = blockFactory; transportService.registerRequestHandler(LOOKUP_ACTION_NAME, this.executor, LookupRequest::new, new TransportHandler()); } @@ -208,7 +212,7 @@ private void doLookup( OutputOperator outputOperator = new OutputOperator(List.of(), Function.identity(), result::set); Driver driver = new Driver( "enrich-lookup:" + sessionId, - new DriverContext(bigArrays), + new DriverContext(bigArrays, blockFactory), () -> lookupDescription(sessionId, shardId, matchType, matchField, extractFields, inputPage.getPositionCount()), queryOperator, intermediateOperators, diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java index d5b8b6df1db8c..700edcf5582c2 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.util.iterable.Iterables; import org.elasticsearch.compute.Describable; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.lucene.DataPartitioning; @@ -108,6 +109,7 @@ public class LocalExecutionPlanner { private final String sessionId; private final CancellableTask parentTask; private final BigArrays bigArrays; + private final BlockFactory blockFactory; private final EsqlConfiguration configuration; private final ExchangeSourceHandler exchangeSourceHandler; private final ExchangeSinkHandler exchangeSinkHandler; @@ -118,6 +120,7 @@ public LocalExecutionPlanner( String sessionId, CancellableTask parentTask, BigArrays bigArrays, + BlockFactory blockFactory, EsqlConfiguration configuration, ExchangeSourceHandler exchangeSourceHandler, ExchangeSinkHandler exchangeSinkHandler, @@ -127,6 +130,7 @@ public LocalExecutionPlanner( this.sessionId = sessionId; this.parentTask = parentTask; this.bigArrays = bigArrays; + this.blockFactory = blockFactory; this.exchangeSourceHandler = exchangeSourceHandler; this.exchangeSinkHandler = exchangeSinkHandler; this.enrichLookupService = enrichLookupService; @@ -144,14 +148,15 @@ public LocalExecutionPlan plan(PhysicalPlan node) { configuration.pragmas().taskConcurrency(), configuration.pragmas().dataPartitioning(), configuration.pragmas().pageSize(), - bigArrays + bigArrays, + blockFactory ); PhysicalOperation physicalOperation = plan(node, context); context.addDriverFactory( new DriverFactory( - new DriverSupplier(context.bigArrays, physicalOperation, configuration.pragmas().statusInterval()), + new DriverSupplier(context.bigArrays, context.blockFactory, physicalOperation, configuration.pragmas().statusInterval()), context.driverParallelism().get() ) ); @@ -659,7 +664,8 @@ public record LocalExecutionPlannerContext( int taskConcurrency, DataPartitioning dataPartitioning, int configuredPageSize, - BigArrays bigArrays + BigArrays bigArrays, + BlockFactory blockFactory ) { void addDriverFactory(DriverFactory driverFactory) { driverFactories.add(driverFactory); @@ -683,7 +689,7 @@ int pageSize(Integer estimatedRowSize) { } } - record DriverSupplier(BigArrays bigArrays, PhysicalOperation physicalOperation, TimeValue statusInterval) + record DriverSupplier(BigArrays bigArrays, BlockFactory blockFactory, PhysicalOperation physicalOperation, TimeValue statusInterval) implements Function, Describable { @@ -693,7 +699,7 @@ public Driver apply(String sessionId) { List operators = new ArrayList<>(); SinkOperator sink = null; boolean success = false; - var driverContext = new DriverContext(bigArrays); + var driverContext = new DriverContext(bigArrays, blockFactory); try { source = physicalOperation.source(driverContext); physicalOperation.operators(operators, driverContext); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java index 5c14a19afd6f2..7d332ce28025d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverTaskRunner; @@ -86,6 +87,8 @@ public class ComputeService { private static final Logger LOGGER = LogManager.getLogger(ComputeService.class); private final SearchService searchService; private final BigArrays bigArrays; + private final BlockFactory blockFactory; + private final TransportService transportService; private final Executor esqlExecutor; private final DriverTaskRunner driverRunner; @@ -98,11 +101,13 @@ public ComputeService( ExchangeService exchangeService, EnrichLookupService enrichLookupService, ThreadPool threadPool, - BigArrays bigArrays + BigArrays bigArrays, + BlockFactory blockFactory ) { this.searchService = searchService; this.transportService = transportService; this.bigArrays = bigArrays.withCircuitBreaking(); + this.blockFactory = blockFactory; this.esqlExecutor = threadPool.executor(ESQL_THREAD_POOL_NAME); transportService.registerRequestHandler(DATA_ACTION_NAME, this.esqlExecutor, DataNodeRequest::new, new DataNodeRequestHandler()); this.driverRunner = new DriverTaskRunner(transportService, this.esqlExecutor); @@ -238,6 +243,7 @@ void runCompute(CancellableTask task, ComputeContext context, PhysicalPlan plan, context.sessionId, task, bigArrays, + blockFactory, context.configuration, context.exchangeSource(), context.exchangeSink(), diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlBlockFactoryParams.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlBlockFactoryParams.java new file mode 100644 index 0000000000000..1ca1d5e217f6a --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlBlockFactoryParams.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.plugin; + +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.NoopCircuitBreaker; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.data.BlockFactoryParameters; + +/** A provider for sharing the given parameters with the compute engine's block factory. */ +public class EsqlBlockFactoryParams implements BlockFactoryParameters { + + static final CircuitBreaker NOOP_BREAKER = new NoopCircuitBreaker("ESQL-noop-breaker"); + + static CircuitBreaker ESQL_BREAKER; + static BigArrays ESQL_BIGARRAYS; + + static void init(BigArrays bigArrays) { + ESQL_BREAKER = bigArrays.breakerService().getBreaker("request"); + ESQL_BIGARRAYS = bigArrays; + } + + final CircuitBreaker breaker; + final BigArrays bigArrays; + + public EsqlBlockFactoryParams() { + this.breaker = ESQL_BREAKER; + this.bigArrays = ESQL_BIGARRAYS; + } + + @Override + public CircuitBreaker breaker() { + return breaker != null ? breaker : NOOP_BREAKER; + } + + @Override + public BigArrays bigArrays() { + return bigArrays != null ? bigArrays : BigArrays.NON_RECYCLING_INSTANCE; + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java index fbaa812f68db7..550e42e715228 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.operator.exchange.ExchangeService; import org.elasticsearch.search.SearchService; import org.elasticsearch.tasks.CancellableTask; @@ -66,14 +67,17 @@ public TransportEsqlQueryAction( this.requestExecutor = threadPool.executor(EsqlPlugin.ESQL_THREAD_POOL_NAME); exchangeService.registerTransportHandler(transportService); this.exchangeService = exchangeService; - this.enrichLookupService = new EnrichLookupService(clusterService, searchService, transportService, bigArrays); + EsqlBlockFactoryParams.init(bigArrays); + var blockFactory = BlockFactory.getGlobalInstance(); + this.enrichLookupService = new EnrichLookupService(clusterService, searchService, transportService, bigArrays, blockFactory); this.computeService = new ComputeService( searchService, transportService, exchangeService, enrichLookupService, threadPool, - bigArrays + bigArrays, + blockFactory ); this.settings = settings; } diff --git a/x-pack/plugin/esql/src/main/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters b/x-pack/plugin/esql/src/main/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters new file mode 100644 index 0000000000000..e397954c84cbe --- /dev/null +++ b/x-pack/plugin/esql/src/main/resources/META-INF/services/org.elasticsearch.compute.data.BlockFactoryParameters @@ -0,0 +1,8 @@ +# +# 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; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +org.elasticsearch.xpack.esql.plugin.EsqlBlockFactoryParams diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index 8a18d7b3a26ed..caf907c94feb5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverRunner; @@ -334,6 +335,7 @@ private ActualResults executePlan() throws Exception { sessionId, new CancellableTask(1, "transport", "esql", null, TaskId.EMPTY_TASK_ID, Map.of()), bigArrays, + BlockFactory.getGlobalInstance(), configuration, exchangeSource, exchangeSink, diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 5efd165b53173..25e6c3672f020 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BlockUtils; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; @@ -618,7 +619,8 @@ private static void writeToTempDir(String subdir, String str, String extension) */ protected DriverContext driverContext() { return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getGlobalInstance() ); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java index efe8e773bfdaa..aa13838b28266 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java @@ -12,6 +12,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; @@ -149,7 +150,8 @@ private static FieldAttribute field(String name, DataType type) { static DriverContext driverContext() { return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking() + new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), + BlockFactory.getGlobalInstance() ); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java index b1965f19e44f5..645833f01ba28 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java @@ -19,6 +19,7 @@ import org.apache.lucene.tests.index.RandomIndexWriter; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.lucene.LuceneSourceOperator; import org.elasticsearch.compute.lucene.LuceneTopNSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -118,6 +119,7 @@ private LocalExecutionPlanner planner() throws IOException { "test", null, BigArrays.NON_RECYCLING_INSTANCE, + BlockFactory.getGlobalInstance(), config(), null, null, From 49410d3bd5f8cfa81d090a905ca5a3325310a8f3 Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Fri, 22 Sep 2023 16:06:33 +0200 Subject: [PATCH 039/155] fix scaling down to 0 ML pods if possible (#99808) fixes an issue in the new autoscaler (serverless) metrics implementation. Ensure it scale down is suggested down to 0 nodes if possible --- .../ml/autoscaling/MlAutoscalingContext.java | 88 ++++--- .../MlAutoscalingResourceTracker.java | 2 +- .../MlAutoscalingResourceTrackerTests.java | 215 ++++++++++++++++++ 3 files changed, 275 insertions(+), 30 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingContext.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingContext.java index 98d5ff6b7b321..57d0084065fa5 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingContext.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingContext.java @@ -48,48 +48,78 @@ class MlAutoscalingContext { final PersistentTasksCustomMetadata persistentTasks; MlAutoscalingContext() { - anomalyDetectionTasks = List.of(); - snapshotUpgradeTasks = List.of(); - dataframeAnalyticsTasks = List.of(); - modelAssignments = Map.of(); - - waitingAnomalyJobs = List.of(); - waitingSnapshotUpgrades = List.of(); - waitingAnalyticsJobs = List.of(); - waitingAllocatedModels = List.of(); + this(List.of(), List.of(), List.of(), Map.of(), List.of(), null); + } - mlNodes = List.of(); - persistentTasks = null; + MlAutoscalingContext( + final Collection> anomalyDetectionTasks, + final Collection> snapshotUpgradeTasks, + final Collection> dataframeAnalyticsTasks, + final Map modelAssignments, + final List mlNodes, + final PersistentTasksCustomMetadata persistentTasks + ) { + this.anomalyDetectionTasks = anomalyDetectionTasks; + this.snapshotUpgradeTasks = snapshotUpgradeTasks; + this.dataframeAnalyticsTasks = dataframeAnalyticsTasks; + this.modelAssignments = modelAssignments; + this.mlNodes = mlNodes; + this.persistentTasks = persistentTasks; + + waitingAnomalyJobs = waitingAnomalyJobs(anomalyDetectionTasks); + waitingSnapshotUpgrades = getWaitingSnapshotUpgrades(snapshotUpgradeTasks); + waitingAnalyticsJobs = getWaitingAnalyticsJobs(dataframeAnalyticsTasks); + waitingAllocatedModels = getWaitingAllocatedModels(modelAssignments); } MlAutoscalingContext(ClusterState clusterState) { - PersistentTasksCustomMetadata tasks = clusterState.getMetadata().custom(PersistentTasksCustomMetadata.TYPE); - anomalyDetectionTasks = anomalyDetectionTasks(tasks); - snapshotUpgradeTasks = snapshotUpgradeTasks(tasks); - dataframeAnalyticsTasks = dataframeAnalyticsTasks(tasks); + persistentTasks = clusterState.getMetadata().custom(PersistentTasksCustomMetadata.TYPE); + + anomalyDetectionTasks = anomalyDetectionTasks(persistentTasks); + snapshotUpgradeTasks = snapshotUpgradeTasks(persistentTasks); + dataframeAnalyticsTasks = dataframeAnalyticsTasks(persistentTasks); modelAssignments = TrainedModelAssignmentMetadata.fromState(clusterState).allAssignments(); - waitingAnomalyJobs = anomalyDetectionTasks.stream() + waitingAnomalyJobs = waitingAnomalyJobs(anomalyDetectionTasks); + waitingSnapshotUpgrades = getWaitingSnapshotUpgrades(snapshotUpgradeTasks); + waitingAnalyticsJobs = getWaitingAnalyticsJobs(dataframeAnalyticsTasks); + waitingAllocatedModels = getWaitingAllocatedModels(modelAssignments); + + mlNodes = getMlNodes(clusterState); + } + + private static List getWaitingAllocatedModels(Map modelAssignments) { + return modelAssignments.entrySet() + .stream() + // TODO: Eventually care about those that are STARTED but not FULLY_ALLOCATED + .filter(e -> e.getValue().getAssignmentState().equals(AssignmentState.STARTING) && e.getValue().getNodeRoutingTable().isEmpty()) + .map(Map.Entry::getKey) + .toList(); + } + + private static List getWaitingAnalyticsJobs( + Collection> dataframeAnalyticsTasks + ) { + return dataframeAnalyticsTasks.stream() .filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment())) - .map(t -> ((OpenJobAction.JobParams) t.getParams()).getJobId()) + .map(t -> ((StartDataFrameAnalyticsAction.TaskParams) t.getParams()).getId()) .toList(); - waitingSnapshotUpgrades = snapshotUpgradeTasks.stream() + } + + private static List getWaitingSnapshotUpgrades( + Collection> snapshotUpgradeTasks + ) { + return snapshotUpgradeTasks.stream() .filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment())) .map(t -> ((SnapshotUpgradeTaskParams) t.getParams()).getJobId()) .toList(); - waitingAnalyticsJobs = dataframeAnalyticsTasks.stream() + } + + private static List waitingAnomalyJobs(Collection> anomalyDetectionTasks) { + return anomalyDetectionTasks.stream() .filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment())) - .map(t -> ((StartDataFrameAnalyticsAction.TaskParams) t.getParams()).getId()) - .toList(); - waitingAllocatedModels = modelAssignments.entrySet() - .stream() - // TODO: Eventually care about those that are STARTED but not FULLY_ALLOCATED - .filter(e -> e.getValue().getAssignmentState().equals(AssignmentState.STARTING) && e.getValue().getNodeRoutingTable().isEmpty()) - .map(Map.Entry::getKey) + .map(t -> ((OpenJobAction.JobParams) t.getParams()).getJobId()) .toList(); - - mlNodes = getMlNodes(clusterState); - persistentTasks = clusterState.getMetadata().custom(PersistentTasksCustomMetadata.TYPE); } private static Collection> anomalyDetectionTasks( diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java index f1a7abce8e87e..7a20605a3bb7e 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java @@ -297,7 +297,7 @@ static void getMemoryAndProcessors( && perNodeAvailableModelMemoryInBytes > 0 && extraModelMemoryInBytes == 0 && extraProcessors == 0 - && modelMemoryBytesSum < perNodeMemoryInBytes * (osStatsPerNode.size() - 1) + && modelMemoryBytesSum <= perNodeMemoryInBytes * (osStatsPerNode.size() - 1) && (perNodeModelMemoryInBytes.size() < osStatsPerNode.size() // a node has no assigned jobs || checkIfOneNodeCouldBeRemoved( perNodeModelMemoryInBytes, diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTrackerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTrackerTests.java index 4e7dac9c3c4fd..5a371f74098aa 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTrackerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTrackerTests.java @@ -15,18 +15,30 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.VersionInformation; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.core.TimeValue; import org.elasticsearch.monitor.os.OsStats; +import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.client.NoOpClient; +import org.elasticsearch.xpack.core.ml.action.StartTrainedModelDeploymentAction; import org.elasticsearch.xpack.core.ml.autoscaling.MlAutoscalingStats; +import org.elasticsearch.xpack.core.ml.inference.assignment.Priority; +import org.elasticsearch.xpack.core.ml.inference.assignment.RoutingInfo; +import org.elasticsearch.xpack.core.ml.inference.assignment.RoutingState; +import org.elasticsearch.xpack.core.ml.inference.assignment.TrainedModelAssignment; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.process.MlMemoryTracker; +import java.net.InetAddress; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -867,6 +879,209 @@ public void testCheckIfOneNodeCouldBeRemovedProcessorAndMemory() { ); } + public void testGetMemoryAndProcessorsScaleDownToZero() throws InterruptedException { + MlAutoscalingContext mlAutoscalingContext = new MlAutoscalingContext(); + MlMemoryTracker mockTracker = mock(MlMemoryTracker.class); + + long memory = randomLongBetween(100, 1_000_000); + long perNodeAvailableModelMemoryInBytes = memory / 2; + + // scale to zero + this.assertAsync( + listener -> MlAutoscalingResourceTracker.getMemoryAndProcessors( + mlAutoscalingContext, + mockTracker, + Map.of( + "ml-1", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ) + ), + perNodeAvailableModelMemoryInBytes, + 10, + MachineLearning.DEFAULT_MAX_OPEN_JOBS_PER_NODE, + listener + ), + stats -> { + assertEquals(memory, stats.perNodeMemoryInBytes()); + assertEquals(1, stats.nodes()); + assertEquals(0, stats.minNodes()); + assertEquals(0, stats.extraSingleNodeProcessors()); + assertEquals(memory, stats.removeNodeMemoryInBytes()); + assertEquals(MachineLearning.NATIVE_EXECUTABLE_CODE_OVERHEAD.getBytes(), stats.perNodeMemoryOverheadInBytes()); + } + ); + + // 3 nodes with no jobs + this.assertAsync( + listener -> MlAutoscalingResourceTracker.getMemoryAndProcessors( + mlAutoscalingContext, + mockTracker, + Map.of( + "ml-1", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ), + "ml-2", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ), + "ml-3", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ) + ), + perNodeAvailableModelMemoryInBytes, + 10, + MachineLearning.DEFAULT_MAX_OPEN_JOBS_PER_NODE, + listener + ), + stats -> { + assertEquals(memory, stats.perNodeMemoryInBytes()); + assertEquals(3, stats.nodes()); + assertEquals(0, stats.minNodes()); + assertEquals(0, stats.extraSingleNodeProcessors()); + assertEquals(memory, stats.removeNodeMemoryInBytes()); + assertEquals(MachineLearning.NATIVE_EXECUTABLE_CODE_OVERHEAD.getBytes(), stats.perNodeMemoryOverheadInBytes()); + } + ); + } + + // scenario: 3 ml nodes, but only 2 have assigned models + public void testGetMemoryAndProcessorsScaleDown() throws InterruptedException { + Map nodeAttr = Map.of( + MachineLearning.MACHINE_MEMORY_NODE_ATTR, + "1000000000", + MachineLearning.MAX_JVM_SIZE_NODE_ATTR, + "400000000", + MachineLearning.ML_CONFIG_VERSION_NODE_ATTR, + "7.2.0" + ); + + MlAutoscalingContext mlAutoscalingContext = new MlAutoscalingContext( + List.of(), + List.of(), + List.of(), + Map.of( + "model-1", + TrainedModelAssignment.Builder.empty( + new StartTrainedModelDeploymentAction.TaskParams( + "model-1", + "model-1-deployment", + 400, + 1, + 2, + 100, + null, + Priority.NORMAL, + 0L, + 0L + ) + ).addRoutingEntry("ml-node-1", new RoutingInfo(1, 1, RoutingState.STARTED, "")).build(), + "model-2", + TrainedModelAssignment.Builder.empty( + new StartTrainedModelDeploymentAction.TaskParams( + "model-2", + "model-2-deployment", + 400, + 1, + 2, + 100, + null, + Priority.NORMAL, + 0L, + 0L + ) + ).addRoutingEntry("ml-node-3", new RoutingInfo(1, 1, RoutingState.STARTED, "")).build() + ), + + List.of( + new DiscoveryNode( + "ml-node-name-1", + "ml-node-1", + new TransportAddress(InetAddress.getLoopbackAddress(), 9300), + nodeAttr, + Set.of(DiscoveryNodeRole.ML_ROLE), + VersionInformation.CURRENT + ), + new DiscoveryNode( + "ml-node-name-3", + "ml-node-3", + new TransportAddress(InetAddress.getLoopbackAddress(), 9300), + nodeAttr, + Set.of(DiscoveryNodeRole.ML_ROLE), + VersionInformation.CURRENT + ) + ), + PersistentTasksCustomMetadata.builder().build() + ); + MlMemoryTracker mockTracker = mock(MlMemoryTracker.class); + + long memory = 1000000000; + long perNodeAvailableModelMemoryInBytes = 600000000; + + this.assertAsync( + listener -> MlAutoscalingResourceTracker.getMemoryAndProcessors( + mlAutoscalingContext, + mockTracker, + Map.of( + "ml-node-1", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ), + "ml-node-2", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ), + "ml-node-3", + new OsStats( + randomNonNegativeLong(), + new OsStats.Cpu(randomShort(), null), + new OsStats.Mem(memory, memory, randomLongBetween(0, memory)), + new OsStats.Swap(randomNonNegativeLong(), randomNonNegativeLong()), + null + ) + ), + perNodeAvailableModelMemoryInBytes, + 10, + MachineLearning.DEFAULT_MAX_OPEN_JOBS_PER_NODE, + listener + ), + stats -> { + assertEquals(memory, stats.perNodeMemoryInBytes()); + assertEquals(3, stats.nodes()); + assertEquals(1, stats.minNodes()); + assertEquals(0, stats.extraSingleNodeProcessors()); + assertEquals(memory, stats.removeNodeMemoryInBytes()); + assertEquals(MachineLearning.NATIVE_EXECUTABLE_CODE_OVERHEAD.getBytes(), stats.perNodeMemoryOverheadInBytes()); + } + ); + } + private void assertAsync(Consumer> function, Consumer furtherTests) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); AtomicBoolean listenerCalled = new AtomicBoolean(false); From 7246624c6ca329071b3c22d77a02fcfca6ff1756 Mon Sep 17 00:00:00 2001 From: John Verwolf Date: Fri, 22 Sep 2023 07:46:54 -0700 Subject: [PATCH 040/155] Fix concurrency issues in testCancelFailedSearchWhenPartialResultDisallowed (#99689) Fix concurrency issues in the test testCancelFailedSearchWhenPartialResultDisallowed while additionally ensuring that cancelation propagates to the other shards. --- .../search/SearchCancellationIT.java | 102 ++++++++++-------- .../AbstractSearchCancellationTestCase.java | 34 +++++- 2 files changed, 90 insertions(+), 46 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java index 8a13ce9988835..4ca20688331a4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java @@ -16,7 +16,9 @@ import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchScrollAction; +import org.elasticsearch.action.search.SearchShardTask; import org.elasticsearch.action.search.SearchTask; +import org.elasticsearch.action.search.SearchTransportService; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.Strings; @@ -25,6 +27,7 @@ import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.ScriptedMetricAggregationBuilder; +import org.elasticsearch.search.internal.ReaderContext; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskCancelledException; import org.elasticsearch.test.AbstractSearchCancellationTestCase; @@ -221,43 +224,12 @@ public void testCancelMultiSearch() throws Exception { } } - /** - * The test `testCancelFailedSearchWhenPartialResultDisallowed` usually fails when concurrency is enabled unless - * the `cancelledLatch.await()` section is commented out. However, this approach seems prone to race conditions. - * Further investigation is needed to determine if this test just needs to be revised, or rather, if it is - * detecting a deeper issue. For now, we will disable concurrency here. - */ - @Override - protected boolean enableConcurrentSearch() { - return false; - } - public void testCancelFailedSearchWhenPartialResultDisallowed() throws Exception { - final List plugins = initBlockFactory(); int numberOfShards = between(2, 5); - AtomicBoolean failed = new AtomicBoolean(); - CountDownLatch queryLatch = new CountDownLatch(1); - CountDownLatch cancelledLatch = new CountDownLatch(1); - for (ScriptedBlockPlugin plugin : plugins) { - plugin.disableBlock(); - plugin.setBeforeExecution(() -> { - try { - queryLatch.await(); // block the query until we get a search task - } catch (InterruptedException e) { - throw new AssertionError(e); - } - if (failed.compareAndSet(false, true)) { - throw new IllegalStateException("simulated"); - } - try { - cancelledLatch.await(); // block the query until the search is cancelled - } catch (InterruptedException e) { - throw new AssertionError(e); - } - }); - } createIndex("test", numberOfShards, 0); indexTestData(); + + // Define (but don't run) the search request, expecting a partial shard failure. We will run it later. Thread searchThread = new Thread(() -> { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, @@ -270,29 +242,59 @@ public void testCancelFailedSearchWhenPartialResultDisallowed() throws Exception ); assertThat(e.getMessage(), containsString("Partial shards failure")); }); + + // When the search request executes, block all shards except 1. + final List searchShardBlockingPlugins = initSearchShardBlockingPlugin(); + AtomicBoolean letOneShardProceed = new AtomicBoolean(); + CountDownLatch shardTaskLatch = new CountDownLatch(1); + for (SearchShardBlockingPlugin plugin : searchShardBlockingPlugins) { + plugin.setRunOnNewReaderContext((ReaderContext c) -> { + if (letOneShardProceed.compareAndSet(false, true)) { + // Let one shard continue. + } else { + try { + shardTaskLatch.await(); // Bock the other shards. + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + }); + } + + // For the shard that was allowed to proceed, have a single query-execution thread throw an exception. + final List plugins = initBlockFactory(); + AtomicBoolean oneThreadWillError = new AtomicBoolean(); + for (ScriptedBlockPlugin plugin : plugins) { + plugin.disableBlock(); + plugin.setBeforeExecution(() -> { + if (oneThreadWillError.compareAndSet(false, true)) { + throw new IllegalStateException("This will cancel the ContextIndexSearcher.search task"); + } + }); + } + + // Now run the search request. searchThread.start(); + try { - assertBusy(() -> assertThat(getSearchTasks(), hasSize(1))); - queryLatch.countDown(); assertBusy(() -> { - final List searchTasks = getSearchTasks(); - // The search request can complete before the "cancelledLatch" is latched if the second shard request is sent - // after the request was cancelled (i.e., the child task is not allowed to start after the parent was cancelled). - if (searchTasks.isEmpty() == false) { - assertThat(searchTasks, hasSize(1)); - assertTrue(searchTasks.get(0).isCancelled()); + final List coordinatorSearchTask = getCoordinatorSearchTasks(); + assertThat("The Coordinator should have one SearchTask.", coordinatorSearchTask, hasSize(1)); + assertTrue("The SearchTask should be cancelled.", coordinatorSearchTask.get(0).isCancelled()); + for (var shardQueryTask : getShardQueryTasks()) { + assertTrue("All SearchShardTasks should then be cancelled", shardQueryTask.isCancelled()); } }, 30, TimeUnit.SECONDS); + shardTaskLatch.countDown(); // unblock the shardTasks, allowing the test to conclude. } finally { + searchThread.join(); for (ScriptedBlockPlugin plugin : plugins) { plugin.setBeforeExecution(() -> {}); } - cancelledLatch.countDown(); - searchThread.join(); } } - List getSearchTasks() { + List getCoordinatorSearchTasks() { List tasks = new ArrayList<>(); for (String nodeName : internalCluster().getNodeNames()) { TransportService transportService = internalCluster().getInstance(TransportService.class, nodeName); @@ -305,4 +307,16 @@ List getSearchTasks() { return tasks; } + List getShardQueryTasks() { + List tasks = new ArrayList<>(); + for (String nodeName : internalCluster().getNodeNames()) { + TransportService transportService = internalCluster().getInstance(TransportService.class, nodeName); + for (Task task : transportService.getTaskManager().getCancellableTasks().values()) { + if (task.getAction().equals(SearchTransportService.QUERY_ACTION_NAME)) { + tasks.add((SearchShardTask) task); + } + } + } + return tasks; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java index 479b81949a3f5..4325ba46f5e86 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java @@ -19,23 +19,26 @@ import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexModule; +import org.elasticsearch.index.shard.SearchOperationListener; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.PluginsService; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.search.SearchService; +import org.elasticsearch.search.internal.ReaderContext; import org.elasticsearch.search.lookup.LeafStoredFieldsLookup; import org.elasticsearch.tasks.TaskInfo; import org.junit.BeforeClass; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; @@ -54,7 +57,7 @@ public static void init() { @Override protected Collection> nodePlugins() { - return Collections.singleton(ScriptedBlockPlugin.class); + return List.of(ScriptedBlockPlugin.class, SearchShardBlockingPlugin.class); } @Override @@ -256,4 +259,31 @@ private Object termScript(Map params) { return 1; } } + + protected List initSearchShardBlockingPlugin() { + List plugins = new ArrayList<>(); + for (PluginsService pluginsService : internalCluster().getInstances(PluginsService.class)) { + plugins.addAll(pluginsService.filterPlugins(SearchShardBlockingPlugin.class)); + } + return plugins; + } + + public static class SearchShardBlockingPlugin extends Plugin { + private final AtomicReference> runOnNewReaderContext = new AtomicReference<>(); + + public void setRunOnNewReaderContext(Consumer consumer) { + runOnNewReaderContext.set(consumer); + } + + @Override + public void onIndexModule(IndexModule indexModule) { + super.onIndexModule(indexModule); + indexModule.addSearchOperationListener(new SearchOperationListener() { + @Override + public void onNewReaderContext(ReaderContext c) { + runOnNewReaderContext.get().accept(c); + } + }); + } + } } From 5f43cd8f46d52af11145a7781cf86c1ec6b348f9 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Fri, 22 Sep 2023 15:52:59 +0100 Subject: [PATCH 041/155] Retry rolling upgrade junit tests (#99760) Re-applies the changes from #99572 to move some bwc tests to a junit-based build infrastructure. Some tests that did not handle the move well have been kept in rolling-upgrade-legacy using the old gradle-based infrastructure --- .../internal/RestrictedBuildApiService.java | 2 +- .../resources/checkstyle_suppressions.xml | 2 +- qa/full-cluster-restart/build.gradle | 5 + qa/rolling-upgrade-legacy/build.gradle | 143 +++++++ .../upgrades/AbstractRollingTestCase.java | 18 - .../elasticsearch/upgrades/RecoveryIT.java | 20 +- .../UpgradeClusterClientYamlTestSuiteIT.java | 5 - .../test/mixed_cluster/10_basic.yml | 0 .../mixed_cluster/20_camel_case_on_format.yml | 0 .../test/mixed_cluster/30_vector_search.yml | 0 .../test/old_cluster/10_basic.yml | 0 .../old_cluster/20_camel_case_on_format.yml | 0 .../test/old_cluster/30_vector_search.yml | 2 - .../test/upgraded_cluster/10_basic.yml | 0 .../20_camel_case_on_format.yml | 0 .../upgraded_cluster/30_vector_search.yml | 0 qa/rolling-upgrade/build.gradle | 135 +------ .../upgrades/DesiredNodesUpgradeIT.java | 65 ++-- .../upgrades/FeatureUpgradeIT.java | 16 +- .../elasticsearch/upgrades/FieldCapsIT.java | 34 +- .../elasticsearch/upgrades/IndexingIT.java | 363 ++++++++---------- .../ParameterizedRollingUpgradeTestCase.java | 226 +++++++++++ .../upgrades/SnapshotBasedRecoveryIT.java | 160 ++++---- .../upgrades/SystemIndicesUpgradeIT.java | 12 +- .../org/elasticsearch/upgrades/TsdbIT.java | 46 ++- .../UpgradeWithOldIndexSettingsIT.java | 132 +++++++ .../org/elasticsearch/upgrades/XPackIT.java | 14 +- .../UpgradeWithOldIndexSettingsIT.java | 131 ------- 28 files changed, 893 insertions(+), 638 deletions(-) create mode 100644 qa/rolling-upgrade-legacy/build.gradle rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java (79%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java (97%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java (95%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml (99%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml (100%) rename qa/{rolling-upgrade => rolling-upgrade-legacy}/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml (100%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java (82%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java (90%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/FieldCapsIT.java (94%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/IndexingIT.java (52%) create mode 100644 qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java (61%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java (95%) rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/TsdbIT.java (90%) create mode 100644 qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java rename qa/rolling-upgrade/src/{test => javaRestTest}/java/org/elasticsearch/upgrades/XPackIT.java (93%) delete mode 100644 qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java index e9438eabadbb6..959bfb8cd3789 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/RestrictedBuildApiService.java @@ -66,7 +66,7 @@ private static ListMultimap, String> createLegacyRestTestBasePluginUsag map.put(LegacyRestTestBasePlugin.class, ":qa:multi-cluster-search"); map.put(LegacyRestTestBasePlugin.class, ":qa:remote-clusters"); map.put(LegacyRestTestBasePlugin.class, ":qa:repository-multi-version"); - map.put(LegacyRestTestBasePlugin.class, ":qa:rolling-upgrade"); + map.put(LegacyRestTestBasePlugin.class, ":qa:rolling-upgrade-legacy"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-http"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-ingest-disabled"); map.put(LegacyRestTestBasePlugin.class, ":qa:smoke-test-ingest-with-all-dependencies"); diff --git a/build-tools-internal/src/main/resources/checkstyle_suppressions.xml b/build-tools-internal/src/main/resources/checkstyle_suppressions.xml index 211faf973b772..9f074513b6d4e 100644 --- a/build-tools-internal/src/main/resources/checkstyle_suppressions.xml +++ b/build-tools-internal/src/main/resources/checkstyle_suppressions.xml @@ -32,7 +32,7 @@ - + diff --git a/qa/full-cluster-restart/build.gradle b/qa/full-cluster-restart/build.gradle index 06442b7cfe6a6..2692cf0f0b05f 100644 --- a/qa/full-cluster-restart/build.gradle +++ b/qa/full-cluster-restart/build.gradle @@ -19,3 +19,8 @@ BuildParams.bwcVersions.withIndexCompatible { bwcVersion, baseName -> systemProperty("tests.old_cluster_version", bwcVersion) } } + +tasks.withType(Test).configureEach { + // CI doesn't like it when there's multiple clusters running at once + maxParallelForks = 1 +} diff --git a/qa/rolling-upgrade-legacy/build.gradle b/qa/rolling-upgrade-legacy/build.gradle new file mode 100644 index 0000000000000..7aca34bef8a1b --- /dev/null +++ b/qa/rolling-upgrade-legacy/build.gradle @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + + +import org.elasticsearch.gradle.Version +import org.elasticsearch.gradle.internal.BwcVersions +import org.elasticsearch.gradle.internal.info.BuildParams +import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask + +apply plugin: 'elasticsearch.internal-testclusters' +apply plugin: 'elasticsearch.standalone-rest-test' +apply plugin: 'elasticsearch.bwc-test' +apply plugin: 'elasticsearch.rest-resources' + +BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> + /* + * NOTE: This module is for the tests that were problematic when converting :qa:rolling-upgrade to the junit-based bwc test definition + * Over time, these should be migrated into the :qa:rolling-upgrade module and fixed properly + * + * The goal here is to: + *
    + *
  • start three nodes on the old version + *
  • run tests with systemProperty 'tests.rest.suite', 'old_cluster' + *
  • upgrade one node + *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' + *
  • upgrade one more node + *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' again + *
  • updgrade the last node + *
  • run tests with systemProperty 'tests.rest.suite', 'upgraded_cluster' + *
+ */ + + def baseCluster = testClusters.register(baseName) { + versions = [bwcVersion.toString(), project.version] + numberOfNodes = 3 + + setting 'repositories.url.allowed_urls', 'http://snapshot.test*' + setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" + setting 'xpack.security.enabled', 'false' + setting 'logger.org.elasticsearch.cluster.service.MasterService', 'TRACE' + setting 'logger.org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator', 'TRACE' + setting 'logger.org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders', 'TRACE' + requiresFeature 'es.index_mode_feature_flag_registered', Version.fromString("8.0.0") + } + + String oldVersion = bwcVersion.toString() + + tasks.register("${baseName}#oldClusterTest", StandaloneRestIntegTestTask) { + dependsOn "processTestResources" + useCluster baseCluster + mustRunAfter("precommit") + doFirst { + delete("${buildDir}/cluster/shared/repo/${baseName}") + } + def excludeList = [] + systemProperty 'tests.rest.suite', 'old_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["old_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["old_cluster/30_vector_search/Create indexed byte vectors and search"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#oneThirdUpgradedTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#oldClusterTest" + useCluster baseCluster + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + systemProperty 'tests.first_round', 'true' + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#twoThirdsUpgradedTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#oneThirdUpgradedTest" + useCluster baseCluster + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + systemProperty 'tests.first_round', 'false' + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register("${baseName}#upgradedClusterTest", StandaloneRestIntegTestTask) { + dependsOn "${baseName}#twoThirdsUpgradedTest" + doFirst { + baseCluster.get().nextNodeToNextVersion() + } + useCluster testClusters.named(baseName) + systemProperty 'tests.rest.suite', 'upgraded_cluster' + systemProperty 'tests.upgrade_from_version', oldVersion + nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) + nonInputProperties.systemProperty('tests.clustername', baseName) + def excludeList = [] + if (bwcVersion.before("8.4.0")) { + excludeList.addAll(["upgraded_cluster/30_vector_search/*"]) + } else if (bwcVersion.before("8.6.0")) { + excludeList.addAll(["upgraded_cluster/30_vector_search/Search byte indices created in old cluster"]) + } + if (excludeList.isEmpty() == false) { + systemProperty 'tests.rest.blacklist', excludeList.join(',') + } + } + + tasks.register(bwcTaskName(bwcVersion)) { + dependsOn tasks.named("${baseName}#upgradedClusterTest") + } +} diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java similarity index 79% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java rename to qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java index 008a718be5873..74a8eb7fd1988 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java +++ b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/AbstractRollingTestCase.java @@ -9,11 +9,8 @@ import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.IndexVersion; import org.elasticsearch.test.rest.ESRestTestCase; -import static org.hamcrest.Matchers.lessThan; - public abstract class AbstractRollingTestCase extends ESRestTestCase { protected enum ClusterType { OLD, @@ -34,16 +31,6 @@ public static ClusterType parse(String value) { protected static final boolean FIRST_MIXED_ROUND = Boolean.parseBoolean(System.getProperty("tests.first_round", "false")); protected static final Version UPGRADE_FROM_VERSION = Version.fromString(System.getProperty("tests.upgrade_from_version")); - protected static IndexVersion getOldClusterIndexVersion() { - var version = UPGRADE_FROM_VERSION; - if (version.equals(org.elasticsearch.Version.CURRENT)) { - return IndexVersion.current(); - } else { - assertThat("Index version needs to be added to rolling test parameters", version, lessThan(org.elasticsearch.Version.V_8_11_0)); - return IndexVersion.fromId(version.id); - } - } - @Override protected final boolean resetFeatureStates() { return false; @@ -54,11 +41,6 @@ protected final boolean preserveIndicesUponCompletion() { return true; } - @Override - protected final boolean preserveDataStreamsUponCompletion() { - return true; - } - @Override protected final boolean preserveReposUponCompletion() { return true; diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java similarity index 97% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java rename to qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java index 39700514cd79f..077eae88fba02 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java @@ -41,7 +41,6 @@ import static org.elasticsearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING; import static org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider.INDEX_ROUTING_ALLOCATION_ENABLE_SETTING; import static org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY; -import static org.elasticsearch.upgrades.UpgradeWithOldIndexSettingsIT.updateIndexSettingsPermittingSlowlogDeprecationWarning; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.in; @@ -747,4 +746,23 @@ public void testSoftDeletesDisabledWarning() throws Exception { ensureGreen(indexName); indexDocs(indexName, randomInt(100), randomInt(100)); } + + /* + * Copied from UpgradeWithOldIndexSettingsIT in the new format + */ + private static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { + Request request = new Request("PUT", "/" + index + "/_settings"); + request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); + if (UPGRADE_FROM_VERSION.before(Version.V_7_17_9)) { + // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) + // Below warnings are set (and leaking) from an index in this test case + request.setOptions(expectVersionSpecificWarnings(v -> { + v.compatible( + "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " + + "See the breaking changes documentation for the next major version." + ); + })); + } + client().performRequest(request); + } } diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java similarity index 95% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java rename to qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java index 0f829f20fe3c4..068747d5a4824 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java +++ b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java @@ -40,11 +40,6 @@ protected boolean preserveTemplatesUponCompletion() { return true; } - @Override - protected boolean preserveDataStreamsUponCompletion() { - return true; - } - public UpgradeClusterClientYamlTestSuiteIT(ClientYamlTestCandidate testCandidate) { super(testCandidate); } diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/mixed_cluster/30_vector_search.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml similarity index 99% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml index 11e9fdc2cca95..b471fa56a47a5 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml +++ b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/old_cluster/30_vector_search.yml @@ -11,7 +11,6 @@ bdv: type: dense_vector dims: 3 - index: false knn: type: dense_vector dims: 3 @@ -126,7 +125,6 @@ bdv: type: dense_vector element_type: byte - index: false dims: 3 knn: type: dense_vector diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/20_camel_case_on_format.yml diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml b/qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml similarity index 100% rename from qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml rename to qa/rolling-upgrade-legacy/src/test/resources/rest-api-spec/test/upgraded_cluster/30_vector_search.yml diff --git a/qa/rolling-upgrade/build.gradle b/qa/rolling-upgrade/build.gradle index d3078dd8c9381..440483039256c 100644 --- a/qa/rolling-upgrade/build.gradle +++ b/qa/rolling-upgrade/build.gradle @@ -6,135 +6,26 @@ * Side Public License, v 1. */ - -import org.elasticsearch.gradle.Version -import org.elasticsearch.gradle.internal.BwcVersions import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask apply plugin: 'elasticsearch.internal-testclusters' -apply plugin: 'elasticsearch.standalone-rest-test' +apply plugin: 'elasticsearch.internal-java-rest-test' +apply plugin: 'elasticsearch.internal-test-artifact-base' apply plugin: 'elasticsearch.bwc-test' -apply plugin: 'elasticsearch.rest-resources' - -BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> - /* - * The goal here is to: - *
    - *
  • start three nodes on the old version - *
  • run tests with systemProperty 'tests.rest.suite', 'old_cluster' - *
  • upgrade one node - *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' - *
  • upgrade one more node - *
  • run tests with systemProperty 'tests.rest.suite', 'mixed_cluster' again - *
  • updgrade the last node - *
  • run tests with systemProperty 'tests.rest.suite', 'upgraded_cluster' - *
- */ - - def baseCluster = testClusters.register(baseName) { - versions = [bwcVersion.toString(), project.version] - numberOfNodes = 3 - - setting 'repositories.url.allowed_urls', 'http://snapshot.test*' - setting 'path.repo', "${buildDir}/cluster/shared/repo/${baseName}" - setting 'xpack.security.enabled', 'false' - setting 'logger.org.elasticsearch.cluster.service.MasterService', 'TRACE' - setting 'logger.org.elasticsearch.cluster.routing.allocation.allocator.DesiredBalanceShardsAllocator', 'TRACE' - setting 'logger.org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders', 'TRACE' - requiresFeature 'es.index_mode_feature_flag_registered', Version.fromString("8.0.0") - } - - String oldVersion = bwcVersion.toString() - - tasks.register("${baseName}#oldClusterTest", StandaloneRestIntegTestTask) { - dependsOn "processTestResources" - useCluster baseCluster - mustRunAfter("precommit") - doFirst { - delete("${buildDir}/cluster/shared/repo/${baseName}") - } - def excludeList = [] - systemProperty 'tests.rest.suite', 'old_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["old_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["old_cluster/30_vector_search/Create indexed byte vectors and search"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - - tasks.register("${baseName}#oneThirdUpgradedTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#oldClusterTest" - useCluster baseCluster - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - systemProperty 'tests.first_round', 'true' - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } - tasks.register("${baseName}#twoThirdsUpgradedTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#oneThirdUpgradedTest" - useCluster baseCluster - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - systemProperty 'tests.first_round', 'false' - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["mixed_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } - } +testArtifacts { + registerTestArtifactFromSourceSet(sourceSets.javaRestTest) +} - tasks.register("${baseName}#upgradedClusterTest", StandaloneRestIntegTestTask) { - dependsOn "${baseName}#twoThirdsUpgradedTest" - doFirst { - baseCluster.get().nextNodeToNextVersion() - } - useCluster testClusters.named(baseName) - systemProperty 'tests.rest.suite', 'upgraded_cluster' - systemProperty 'tests.upgrade_from_version', oldVersion - nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c -> c.allHttpSocketURI.join(","))) - nonInputProperties.systemProperty('tests.clustername', baseName) - def excludeList = [] - if (bwcVersion.before("8.4.0")) { - excludeList.addAll(["upgraded_cluster/30_vector_search/*"]) - } else if (bwcVersion.before("8.6.0")) { - excludeList.addAll(["upgraded_cluster/30_vector_search/Search byte indices created in old cluster"]) - } - if (excludeList.isEmpty() == false) { - systemProperty 'tests.rest.blacklist', excludeList.join(',') - } +BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> + tasks.register(bwcTaskName(bwcVersion), StandaloneRestIntegTestTask) { + usesBwcDistribution(bwcVersion) + systemProperty("tests.old_cluster_version", bwcVersion) } +} - tasks.register(bwcTaskName(bwcVersion)) { - dependsOn tasks.named("${baseName}#upgradedClusterTest") - } +tasks.withType(Test).configureEach { + // CI doesn't like it when there's multiple clusters running at once + maxParallelForks = 1 } diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java similarity index 82% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java index 5bafccf7aee1b..15fc1e68196e1 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/DesiredNodesUpgradeIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.desirednodes.UpdateDesiredNodesRequest; import org.elasticsearch.client.Request; @@ -31,18 +33,26 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -public class DesiredNodesUpgradeIT extends AbstractRollingTestCase { +public class DesiredNodesUpgradeIT extends ParameterizedRollingUpgradeTestCase { + + private final int desiredNodesVersion; + + public DesiredNodesUpgradeIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + desiredNodesVersion = upgradedNodes + 1; + } + private enum ProcessorsPrecision { DOUBLE, FLOAT } public void testUpgradeDesiredNodes() throws Exception { - assumeTrue("Desired nodes was introduced in 8.1", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_1_0)); + assumeTrue("Desired nodes was introduced in 8.1", getOldClusterVersion().onOrAfter(Version.V_8_1_0)); - if (UPGRADE_FROM_VERSION.onOrAfter(Processors.DOUBLE_PROCESSORS_SUPPORT_VERSION)) { + if (getOldClusterVersion().onOrAfter(Processors.DOUBLE_PROCESSORS_SUPPORT_VERSION)) { assertUpgradedNodesCanReadDesiredNodes(); - } else if (UPGRADE_FROM_VERSION.onOrAfter(DesiredNode.RANGE_FLOAT_PROCESSORS_SUPPORT_VERSION)) { + } else if (getOldClusterVersion().onOrAfter(DesiredNode.RANGE_FLOAT_PROCESSORS_SUPPORT_VERSION)) { assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent(); } else { assertDesiredNodesWithFloatProcessorsAreRejectedInOlderVersions(); @@ -50,13 +60,7 @@ public void testUpgradeDesiredNodes() throws Exception { } private void assertUpgradedNodesCanReadDesiredNodes() throws Exception { - final int desiredNodesVersion = switch (CLUSTER_TYPE) { - case OLD -> 1; - case MIXED -> FIRST_MIXED_ROUND ? 2 : 3; - case UPGRADED -> 4; - }; - - if (CLUSTER_TYPE != ClusterType.OLD) { + if (isMixedCluster() || isUpgradedCluster()) { final Map desiredNodes = getLatestDesiredNodes(); final String historyId = extractValue(desiredNodes, "history_id"); final int version = extractValue(desiredNodes, "version"); @@ -83,13 +87,7 @@ private void assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent() throws ) .toList(); - final int desiredNodesVersion = switch (CLUSTER_TYPE) { - case OLD -> 1; - case MIXED -> FIRST_MIXED_ROUND ? 2 : 3; - case UPGRADED -> 4; - }; - - if (CLUSTER_TYPE != ClusterType.OLD) { + if (isMixedCluster() || isUpgradedCluster()) { updateDesiredNodes(desiredNodes, desiredNodesVersion - 1); } for (int i = 0; i < 2; i++) { @@ -100,28 +98,25 @@ private void assertDesiredNodesUpdatedWithRoundedUpFloatsAreIdempotent() throws final int latestDesiredNodesVersion = extractValue(latestDesiredNodes, "version"); assertThat(latestDesiredNodesVersion, is(equalTo(desiredNodesVersion))); - if (CLUSTER_TYPE == ClusterType.UPGRADED) { + if (isUpgradedCluster()) { assertAllDesiredNodesAreActualized(); } } private void assertDesiredNodesWithFloatProcessorsAreRejectedInOlderVersions() throws Exception { - switch (CLUSTER_TYPE) { - case OLD -> addClusterNodesToDesiredNodesWithIntegerProcessors(1); - case MIXED -> { - int version = FIRST_MIXED_ROUND ? 2 : 3; - // Processor ranges or float processors are forbidden during upgrades: 8.2 -> 8.3 clusters - final var responseException = expectThrows( - ResponseException.class, - () -> addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(version, ProcessorsPrecision.FLOAT) - ); - final var statusCode = responseException.getResponse().getStatusLine().getStatusCode(); - assertThat(statusCode, is(equalTo(400))); - } - case UPGRADED -> { - assertAllDesiredNodesAreActualized(); - addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(4, ProcessorsPrecision.FLOAT); - } + if (isOldCluster()) { + addClusterNodesToDesiredNodesWithIntegerProcessors(1); + } else if (isMixedCluster()) { + // Processor ranges or float processors are forbidden during upgrades: 8.2 -> 8.3 clusters + final var responseException = expectThrows( + ResponseException.class, + () -> addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(desiredNodesVersion, ProcessorsPrecision.FLOAT) + ); + final var statusCode = responseException.getResponse().getStatusLine().getStatusCode(); + assertThat(statusCode, is(equalTo(400))); + } else { + assertAllDesiredNodesAreActualized(); + addClusterNodesToDesiredNodesWithProcessorsOrProcessorRanges(4, ProcessorsPrecision.FLOAT); } getLatestDesiredNodes(); diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java similarity index 90% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java index 588802fb50709..afe3c1c5d22f1 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FeatureUpgradeIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.elasticsearch.action.admin.cluster.migration.TransportGetFeatureUpgradeStatusAction; import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; @@ -21,14 +23,17 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -public class FeatureUpgradeIT extends AbstractRollingTestCase { +public class FeatureUpgradeIT extends ParameterizedRollingUpgradeTestCase { + + public FeatureUpgradeIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } - @SuppressWarnings("unchecked") public void testGetFeatureUpgradeStatus() throws Exception { final String systemIndexWarning = "this request accesses system indices: [.tasks], but in a future major version, direct " + "access to system indices will be prevented by default"; - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { // setup - put something in the tasks index // create index Request createTestIndex = new Request("PUT", "/feature_test_index_old"); @@ -79,7 +84,7 @@ public void testGetFeatureUpgradeStatus() throws Exception { } }); - } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { + } else if (isUpgradedCluster()) { // check results assertBusy(() -> { Request clusterStateRequest = new Request("GET", "/_migration/system_features"); @@ -95,7 +100,7 @@ public void testGetFeatureUpgradeStatus() throws Exception { assertThat(feature, aMapWithSize(4)); assertThat(feature.get("minimum_index_version"), equalTo(getOldClusterIndexVersion().toString())); - if (UPGRADE_FROM_VERSION.before(TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION)) { + if (getOldClusterVersion().before(TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION)) { assertThat(feature.get("migration_status"), equalTo("MIGRATION_NEEDED")); } else { assertThat(feature.get("migration_status"), equalTo("NO_MIGRATION_NEEDED")); @@ -103,5 +108,4 @@ public void testGetFeatureUpgradeStatus() throws Exception { }); } } - } diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java similarity index 94% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java index 83865222a8867..e269b608930a8 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/FieldCapsIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.apache.http.HttpHost; import org.elasticsearch.Version; import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse; @@ -36,15 +38,17 @@ * In 8.2 we also added the ability to filter fields by type and metadata, with some post-hoc filtering applied on * the co-ordinating node if older nodes were included in the system */ -public class FieldCapsIT extends AbstractRollingTestCase { - private static boolean indicesCreated = false; +public class FieldCapsIT extends ParameterizedRollingUpgradeTestCase { + + public FieldCapsIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } + + private static boolean oldIndicesCreated; + private static boolean newIndicesCreated; @Before public void setupIndices() throws Exception { - if (indicesCreated) { - return; - } - indicesCreated = true; final String redMapping = """ "properties": { "red_field": { "type": "keyword" }, @@ -63,7 +67,7 @@ public void setupIndices() throws Exception { "timestamp": {"type": "date"} } """; - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster() && oldIndicesCreated == false) { createIndex("old_red_1", Settings.EMPTY, redMapping); createIndex("old_red_2", Settings.EMPTY, redMapping); createIndex("old_red_empty", Settings.EMPTY, redMapping); @@ -78,7 +82,8 @@ public void setupIndices() throws Exception { ); assertOK(client().performRequest(indexRequest)); } - } else if (CLUSTER_TYPE == ClusterType.MIXED && FIRST_MIXED_ROUND) { + oldIndicesCreated = true; + } else if (isFirstMixedCluster() && newIndicesCreated == false) { createIndex("new_red_1", Settings.EMPTY, redMapping); createIndex("new_red_2", Settings.EMPTY, redMapping); createIndex("new_red_empty", Settings.EMPTY, redMapping); @@ -93,6 +98,7 @@ public void setupIndices() throws Exception { ); assertOK(client().performRequest(indexRequest)); } + newIndicesCreated = true; } } @@ -149,7 +155,7 @@ public void testOldIndicesWithIndexFilter() throws Exception { } public void testNewIndicesOnly() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); { FieldCapabilitiesResponse resp = fieldCaps(List.of("new_red_*"), List.of("*"), null, null, null); assertThat(resp.getIndices(), equalTo(new String[] { "new_red_1", "new_red_2", "new_red_empty" })); @@ -177,7 +183,7 @@ public void testNewIndicesOnly() throws Exception { } public void testNewIndicesOnlyWithIndexFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); final QueryBuilder indexFilter = QueryBuilders.rangeQuery("timestamp").gte("2020-01-01").lte("2020-12-12"); { FieldCapabilitiesResponse resp = fieldCaps(List.of("new_red_*"), List.of("*"), indexFilter, null, null); @@ -203,7 +209,7 @@ public void testNewIndicesOnlyWithIndexFilter() throws Exception { } public void testAllIndices() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); FieldCapabilitiesResponse resp = fieldCaps(List.of("old_*", "new_*"), List.of("*"), null, null, null); assertThat( resp.getIndices(), @@ -235,7 +241,7 @@ public void testAllIndices() throws Exception { } public void testAllIndicesWithIndexFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); final QueryBuilder indexFilter = QueryBuilders.rangeQuery("timestamp").gte("2020-01-01").lte("2020-12-12"); FieldCapabilitiesResponse resp = fieldCaps(List.of("old_*", "new_*"), List.of("*"), indexFilter, null, null); assertThat( @@ -285,7 +291,7 @@ private RestClient getUpgradedNodeClient() throws IOException { // because we are testing that the upgraded node will correctly apply filtering // to responses from older nodes that don't understand the filter parameters public void testAllIndicesWithFieldTypeFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); RestClient restClient = getUpgradedNodeClient(); FieldCapabilitiesResponse resp = fieldCaps(restClient, List.of("old_*", "new_*"), List.of("*"), null, "keyword", null); assertThat(resp.getField("red_field").keySet(), contains("keyword")); @@ -298,7 +304,7 @@ public void testAllIndicesWithFieldTypeFilter() throws Exception { // because we are testing that the upgraded node will correctly apply filtering // to responses from older nodes that don't understand the filter parameters public void testAllIndicesWithExclusionFilter() throws Exception { - assumeFalse("required mixed or upgraded cluster", CLUSTER_TYPE == ClusterType.OLD); + assumeFalse("required mixed or upgraded cluster", isOldCluster()); RestClient client = getUpgradedNodeClient(); { FieldCapabilitiesResponse resp = fieldCaps(client, List.of("old_*", "new_*"), List.of("*"), null, null, null); diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java similarity index 52% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java index b860e53d447b5..73ab8fb0c25d2 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexingIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java @@ -7,6 +7,8 @@ */ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.apache.http.util.EntityUtils; import org.elasticsearch.Version; import org.elasticsearch.client.Request; @@ -15,7 +17,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.common.xcontent.support.XContentMapValues; -import org.elasticsearch.core.Booleans; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.test.ListMatcher; import org.elasticsearch.xcontent.XContentBuilder; @@ -40,39 +41,36 @@ /** * Basic test that indexed documents survive the rolling restart. See - * {@link RecoveryIT} for much more in depth testing of the mechanism + * {@code RecoveryIT} for much more in depth testing of the mechanism * by which they survive. *

* This test is an almost exact copy of IndexingIT in the * xpack rolling restart tests. We should work on a way to remove this * duplication but for now we have no real way to share code. */ -public class IndexingIT extends AbstractRollingTestCase { +public class IndexingIT extends ParameterizedRollingUpgradeTestCase { + + public IndexingIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } public void testIndexing() throws IOException { - switch (CLUSTER_TYPE) { - case OLD: - break; - case MIXED: - Request waitForYellow = new Request("GET", "/_cluster/health"); - waitForYellow.addParameter("wait_for_nodes", "3"); - waitForYellow.addParameter("wait_for_status", "yellow"); - client().performRequest(waitForYellow); - break; - case UPGRADED: - Request waitForGreen = new Request("GET", "/_cluster/health/test_index,index_with_replicas,empty_index"); - waitForGreen.addParameter("wait_for_nodes", "3"); - waitForGreen.addParameter("wait_for_status", "green"); - // wait for long enough that we give delayed unassigned shards to stop being delayed - waitForGreen.addParameter("timeout", "70s"); - waitForGreen.addParameter("level", "shards"); - client().performRequest(waitForGreen); - break; - default: - throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + if (isMixedCluster()) { + Request waitForYellow = new Request("GET", "/_cluster/health"); + waitForYellow.addParameter("wait_for_nodes", "3"); + waitForYellow.addParameter("wait_for_status", "yellow"); + client().performRequest(waitForYellow); + } else if (isUpgradedCluster()) { + Request waitForGreen = new Request("GET", "/_cluster/health/test_index,index_with_replicas,empty_index"); + waitForGreen.addParameter("wait_for_nodes", "3"); + waitForGreen.addParameter("wait_for_status", "green"); + // wait for long enough that we give delayed unassigned shards to stop being delayed + waitForGreen.addParameter("timeout", "70s"); + waitForGreen.addParameter("level", "shards"); + client().performRequest(waitForGreen); } - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { Request createTestIndex = new Request("PUT", "/test_index"); createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); useIgnoreMultipleMatchingTemplatesWarningsHandler(createTestIndex); @@ -95,30 +93,20 @@ public void testIndexing() throws IOException { } int expectedCount; - switch (CLUSTER_TYPE) { - case OLD: - expectedCount = 5; - break; - case MIXED: - if (Booleans.parseBoolean(System.getProperty("tests.first_round"))) { - expectedCount = 5; - } else { - expectedCount = 10; - } - break; - case UPGRADED: - expectedCount = 15; - break; - default: - throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + if (isOldCluster() || isFirstMixedCluster()) { + expectedCount = 5; + } else if (isMixedCluster()) { + expectedCount = 10; + } else { + expectedCount = 15; } assertCount("test_index", expectedCount); assertCount("index_with_replicas", 5); assertCount("empty_index", 0); - if (CLUSTER_TYPE != ClusterType.OLD) { - bulk("test_index", "_" + CLUSTER_TYPE, 5); + if (isOldCluster() == false) { + bulk("test_index", "_" + (isMixedCluster() ? "MIXED" : "UPGRADED"), 5); Request toBeDeleted = new Request("PUT", "/test_index/_doc/to_be_deleted"); toBeDeleted.addParameter("refresh", "true"); toBeDeleted.setJsonEntity("{\"f1\": \"delete-me\"}"); @@ -143,82 +131,76 @@ public void testAutoIdWithOpTypeCreate() throws IOException { bulk.addParameter("refresh", "true"); bulk.setJsonEntity(b); - switch (CLUSTER_TYPE) { - case OLD -> { - Request createTestIndex = new Request("PUT", "/" + indexName); - createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); - client().performRequest(createTestIndex); - } - case MIXED -> { - Request waitForGreen = new Request("GET", "/_cluster/health"); - waitForGreen.addParameter("wait_for_nodes", "3"); - client().performRequest(waitForGreen); - Version minNodeVersion = minNodeVersion(); - if (minNodeVersion.before(Version.V_7_5_0)) { - ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(bulk)); - assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); - assertThat( - e.getMessage(), - // if request goes to 7.5+ node - either(containsString("optype create not supported for indexing requests without explicit id until")) - // if request goes to < 7.5 node - .or(containsString("an id must be provided if version type or value are set")) - ); - } else { - client().performRequest(bulk); - } + if (isOldCluster()) { + Request createTestIndex = new Request("PUT", "/" + indexName); + createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); + client().performRequest(createTestIndex); + } else if (isMixedCluster()) { + Request waitForGreen = new Request("GET", "/_cluster/health"); + waitForGreen.addParameter("wait_for_nodes", "3"); + client().performRequest(waitForGreen); + Version minNodeVersion = minNodeVersion(); + if (minNodeVersion.before(Version.V_7_5_0)) { + ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(bulk)); + assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); + assertThat( + e.getMessage(), + // if request goes to 7.5+ node + either(containsString("optype create not supported for indexing requests without explicit id until")) + // if request goes to < 7.5 node + .or(containsString("an id must be provided if version type or value are set")) + ); + } else { + client().performRequest(bulk); } - case UPGRADED -> client().performRequest(bulk); - default -> throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } else if (isUpgradedCluster()) { + client().performRequest(bulk); } } public void testDateNanosFormatUpgrade() throws IOException { final String indexName = "test_date_nanos"; - switch (CLUSTER_TYPE) { - case OLD -> { - Request createIndex = new Request("PUT", "/" + indexName); - XContentBuilder mappings = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .startObject("mappings") - .startObject("properties") - .startObject("date") - .field("type", "date") - .endObject() - .startObject("date_nanos") - .field("type", "date_nanos") - .endObject() - .endObject() - .endObject() - .endObject(); - createIndex.setJsonEntity(Strings.toString(mappings)); - client().performRequest(createIndex); - Request index = new Request("POST", "/" + indexName + "/_doc/"); - XContentBuilder doc = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .field("date", "2015-01-01T12:10:30.123456789Z") - .field("date_nanos", "2015-01-01T12:10:30.123456789Z") - .endObject(); - index.addParameter("refresh", "true"); - index.setJsonEntity(Strings.toString(doc)); - client().performRequest(index); - } - case UPGRADED -> { - Request search = new Request("POST", "/" + indexName + "/_search"); - XContentBuilder query = XContentBuilder.builder(XContentType.JSON.xContent()) - .startObject() - .array("fields", new String[] { "date", "date_nanos" }) - .endObject(); - search.setJsonEntity(Strings.toString(query)); - Map response = entityAsMap(client().performRequest(search)); - Map bestHit = (Map) ((List) (XContentMapValues.extractValue("hits.hits", response))).get(0); - List date = (List) XContentMapValues.extractValue("fields.date", bestHit); - assertThat(date.size(), equalTo(1)); - assertThat(date.get(0), equalTo("2015-01-01T12:10:30.123Z")); - List dateNanos = (List) XContentMapValues.extractValue("fields.date_nanos", bestHit); - assertThat(dateNanos.size(), equalTo(1)); - assertThat(dateNanos.get(0), equalTo("2015-01-01T12:10:30.123456789Z")); - } + if (isOldCluster()) { + Request createIndex = new Request("PUT", "/" + indexName); + XContentBuilder mappings = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .startObject("mappings") + .startObject("properties") + .startObject("date") + .field("type", "date") + .endObject() + .startObject("date_nanos") + .field("type", "date_nanos") + .endObject() + .endObject() + .endObject() + .endObject(); + createIndex.setJsonEntity(Strings.toString(mappings)); + client().performRequest(createIndex); + Request index = new Request("POST", "/" + indexName + "/_doc/"); + XContentBuilder doc = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .field("date", "2015-01-01T12:10:30.123456789Z") + .field("date_nanos", "2015-01-01T12:10:30.123456789Z") + .endObject(); + index.addParameter("refresh", "true"); + index.setJsonEntity(Strings.toString(doc)); + client().performRequest(index); + } else if (isUpgradedCluster()) { + Request search = new Request("POST", "/" + indexName + "/_search"); + XContentBuilder query = XContentBuilder.builder(XContentType.JSON.xContent()) + .startObject() + .array("fields", new String[] { "date", "date_nanos" }) + .endObject(); + search.setJsonEntity(Strings.toString(query)); + Map response = entityAsMap(client().performRequest(search)); + Map bestHit = (Map) ((List) (XContentMapValues.extractValue("hits.hits", response))).get(0); + List date = (List) XContentMapValues.extractValue("fields.date", bestHit); + assertThat(date.size(), equalTo(1)); + assertThat(date.get(0), equalTo("2015-01-01T12:10:30.123Z")); + List dateNanos = (List) XContentMapValues.extractValue("fields.date_nanos", bestHit); + assertThat(dateNanos.size(), equalTo(1)); + assertThat(dateNanos.get(0), equalTo("2015-01-01T12:10:30.123456789Z")); } } @@ -247,51 +229,45 @@ private void bulk(String index, String valueSuffix, int count) throws IOExceptio } public void testTsdb() throws IOException { - assumeTrue("indexing time series indices changed in 8.2.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_2_0)); + assumeTrue("indexing time series indices changed in 8.2.0", getOldClusterVersion().onOrAfter(Version.V_8_2_0)); StringBuilder bulk = new StringBuilder(); - switch (CLUSTER_TYPE) { - case OLD -> { - createTsdbIndex(); - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[0], TSDB_TIMES[1], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[0], TSDB_TIMES[1], -0.1); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(215.95, 0.005), closeTo(-215.95, 0.005)); - return; - } - case MIXED -> { - if (FIRST_MIXED_ROUND) { - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[1], TSDB_TIMES[2], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[1], TSDB_TIMES[2], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[0], TSDB_TIMES[2], 1.1); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(217.45, 0.005), closeTo(-217.45, 0.005), closeTo(2391.95, 0.005)); - return; - } - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[2], TSDB_TIMES[3], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[2], TSDB_TIMES[3], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[2], TSDB_TIMES[3], 1.1); - tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[0], TSDB_TIMES[3], 10); - bulk("tsdb", bulk.toString()); - assertTsdbAgg(closeTo(218.95, 0.005), closeTo(-218.95, 0.005), closeTo(2408.45, 0.005), closeTo(21895, 0.5)); - return; - } - case UPGRADED -> { - tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[3], TSDB_TIMES[4], 0.1); - tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[3], TSDB_TIMES[4], -0.1); - tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[3], TSDB_TIMES[4], 1.1); - tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[3], TSDB_TIMES[4], 10); - tsdbBulk(bulk, TSDB_DIMS.get(4), TSDB_TIMES[0], TSDB_TIMES[4], -5); - bulk("tsdb", bulk.toString()); - assertTsdbAgg( - closeTo(220.45, 0.005), - closeTo(-220.45, 0.005), - closeTo(2424.95, 0.005), - closeTo(22045, 0.5), - closeTo(-11022.5, 0.5) - ); - return; - } + if (isOldCluster()) { + createTsdbIndex(); + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[0], TSDB_TIMES[1], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[0], TSDB_TIMES[1], -0.1); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(215.95, 0.005), closeTo(-215.95, 0.005)); + return; + } else if (isFirstMixedCluster()) { + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[1], TSDB_TIMES[2], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[1], TSDB_TIMES[2], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[0], TSDB_TIMES[2], 1.1); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(217.45, 0.005), closeTo(-217.45, 0.005), closeTo(2391.95, 0.005)); + + } else if (isMixedCluster()) { + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[2], TSDB_TIMES[3], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[2], TSDB_TIMES[3], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[2], TSDB_TIMES[3], 1.1); + tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[0], TSDB_TIMES[3], 10); + bulk("tsdb", bulk.toString()); + assertTsdbAgg(closeTo(218.95, 0.005), closeTo(-218.95, 0.005), closeTo(2408.45, 0.005), closeTo(21895, 0.5)); + return; + } else { + tsdbBulk(bulk, TSDB_DIMS.get(0), TSDB_TIMES[3], TSDB_TIMES[4], 0.1); + tsdbBulk(bulk, TSDB_DIMS.get(1), TSDB_TIMES[3], TSDB_TIMES[4], -0.1); + tsdbBulk(bulk, TSDB_DIMS.get(2), TSDB_TIMES[3], TSDB_TIMES[4], 1.1); + tsdbBulk(bulk, TSDB_DIMS.get(3), TSDB_TIMES[3], TSDB_TIMES[4], 10); + tsdbBulk(bulk, TSDB_DIMS.get(4), TSDB_TIMES[0], TSDB_TIMES[4], -5); + bulk("tsdb", bulk.toString()); + assertTsdbAgg( + closeTo(220.45, 0.005), + closeTo(-220.45, 0.005), + closeTo(2424.95, 0.005), + closeTo(22045, 0.5), + closeTo(-11022.5, 0.5) + ); } } @@ -361,67 +337,60 @@ private void assertTsdbAgg(Matcher... expected) throws IOException { } public void testSyntheticSource() throws IOException { - assumeTrue("added in 8.4.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_4_0)); - - switch (CLUSTER_TYPE) { - case OLD -> { - Request createIndex = new Request("PUT", "/synthetic"); - XContentBuilder indexSpec = XContentBuilder.builder(XContentType.JSON.xContent()).startObject(); - indexSpec.startObject("mappings"); - { - indexSpec.startObject("_source").field("mode", "synthetic").endObject(); - indexSpec.startObject("properties").startObject("kwd").field("type", "keyword").endObject().endObject(); - } - indexSpec.endObject(); - createIndex.setJsonEntity(Strings.toString(indexSpec.endObject())); - client().performRequest(createIndex); - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "old"}} - {"kwd": "old", "int": -12} - """); - break; - } - case MIXED -> { - if (FIRST_MIXED_ROUND) { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "mixed_1"}} - {"kwd": "mixed_1", "int": 22} - """); - } else { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "mixed_2"}} - {"kwd": "mixed_2", "int": 33} - """); - } - break; - } - case UPGRADED -> { - bulk("synthetic", """ - {"index": {"_index": "synthetic", "_id": "new"}} - {"kwd": "new", "int": 21341325} - """); + assumeTrue("added in 8.4.0", getOldClusterVersion().onOrAfter(Version.V_8_4_0)); + + if (isOldCluster()) { + Request createIndex = new Request("PUT", "/synthetic"); + XContentBuilder indexSpec = XContentBuilder.builder(XContentType.JSON.xContent()).startObject(); + indexSpec.startObject("mappings"); + { + indexSpec.startObject("_source").field("mode", "synthetic").endObject(); + indexSpec.startObject("properties").startObject("kwd").field("type", "keyword").endObject().endObject(); } + indexSpec.endObject(); + createIndex.setJsonEntity(Strings.toString(indexSpec.endObject())); + client().performRequest(createIndex); + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "old"}} + {"kwd": "old", "int": -12} + """); + } else if (isFirstMixedCluster()) { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "mixed_1"}} + {"kwd": "mixed_1", "int": 22} + """); + } else if (isMixedCluster()) { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "mixed_2"}} + {"kwd": "mixed_2", "int": 33} + """); + + } else { + bulk("synthetic", """ + {"index": {"_index": "synthetic", "_id": "new"}} + {"kwd": "new", "int": 21341325} + """); } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/old"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "old").entry("int", -12)) ); - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { return; } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/mixed_1"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "mixed_1").entry("int", 22)) ); - if (CLUSTER_TYPE == ClusterType.MIXED && FIRST_MIXED_ROUND) { + if (isFirstMixedCluster()) { return; } assertMap( entityAsMap(client().performRequest(new Request("GET", "/synthetic/_doc/mixed_2"))), matchesMap().extraOk().entry("_source", matchesMap().entry("kwd", "mixed_2").entry("int", 33)) ); - if (CLUSTER_TYPE == ClusterType.MIXED) { + if (isMixedCluster()) { return; } assertMap( diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java new file mode 100644 index 0000000000000..26b1f8a0153b6 --- /dev/null +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.upgrades; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.test.cluster.ElasticsearchCluster; +import org.elasticsearch.test.cluster.FeatureFlag; +import org.elasticsearch.test.cluster.local.distribution.DistributionType; +import org.elasticsearch.test.cluster.util.Version; +import org.elasticsearch.test.rest.ESRestTestCase; +import org.elasticsearch.test.rest.ObjectPath; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.rules.RuleChain; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestRule; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +public abstract class ParameterizedRollingUpgradeTestCase extends ESRestTestCase { + private static final Version OLD_CLUSTER_VERSION = Version.fromString(System.getProperty("tests.old_cluster_version")); + + private static final TemporaryFolder repoDirectory = new TemporaryFolder(); + + private static final int NODE_NUM = 3; + + private static final ElasticsearchCluster cluster = ElasticsearchCluster.local() + .distribution(DistributionType.DEFAULT) + .version(getOldClusterTestVersion()) + .nodes(NODE_NUM) + .setting("path.repo", new Supplier<>() { + @Override + @SuppressForbidden(reason = "TemporaryFolder only has io.File methods, not nio.File") + public String get() { + return repoDirectory.getRoot().getPath(); + } + }) + .setting("xpack.security.enabled", "false") + .feature(FeatureFlag.TIME_SERIES_MODE) + .build(); + + @ClassRule + public static TestRule ruleChain = RuleChain.outerRule(repoDirectory).around(cluster); + + @ParametersFactory(shuffle = false) + public static Iterable parameters() { + return IntStream.rangeClosed(0, NODE_NUM).boxed().map(n -> new Object[] { n }).toList(); + } + + private static final Set upgradedNodes = new HashSet<>(); + private static boolean upgradeFailed = false; + private static IndexVersion oldIndexVersion; + + private final int requestedUpgradedNodes; + + protected ParameterizedRollingUpgradeTestCase(@Name("upgradedNodes") int upgradedNodes) { + this.requestedUpgradedNodes = upgradedNodes; + } + + @Before + public void extractOldIndexVersion() throws Exception { + if (oldIndexVersion == null && upgradedNodes.isEmpty()) { + IndexVersion indexVersion = null; // these should all be the same version + + Request request = new Request("GET", "_nodes"); + request.addParameter("filter_path", "nodes.*.index_version,nodes.*.name"); + Response response = client().performRequest(request); + ObjectPath objectPath = ObjectPath.createFromResponse(response); + Map nodeMap = objectPath.evaluate("nodes"); + for (String id : nodeMap.keySet()) { + Number ix = objectPath.evaluate("nodes." + id + ".index_version"); + IndexVersion version; + if (ix != null) { + version = IndexVersion.fromId(ix.intValue()); + } else { + // it doesn't have index version (pre 8.11) - just infer it from the release version + version = IndexVersion.fromId(getOldClusterVersion().id); + } + + if (indexVersion == null) { + indexVersion = version; + } else { + String name = objectPath.evaluate("nodes." + id + ".name"); + assertThat("Node " + name + " has a different index version to other nodes", version, equalTo(indexVersion)); + } + } + + assertThat("Index version could not be read", indexVersion, notNullValue()); + oldIndexVersion = indexVersion; + } + } + + @Before + public void upgradeNode() throws Exception { + // Skip remaining tests if upgrade failed + assumeFalse("Cluster upgrade failed", upgradeFailed); + + if (upgradedNodes.size() < requestedUpgradedNodes) { + closeClients(); + // we might be running a specific upgrade test by itself - check previous nodes too + for (int n = 0; n < requestedUpgradedNodes; n++) { + if (upgradedNodes.add(n)) { + try { + logger.info("Upgrading node {} to version {}", n, Version.CURRENT); + cluster.upgradeNodeToVersion(n, Version.CURRENT); + } catch (Exception e) { + upgradeFailed = true; + throw e; + } + } + } + initClient(); + } + } + + @AfterClass + public static void resetNodes() { + oldIndexVersion = null; + upgradedNodes.clear(); + upgradeFailed = false; + } + + protected static org.elasticsearch.Version getOldClusterVersion() { + return org.elasticsearch.Version.fromString(OLD_CLUSTER_VERSION.toString()); + } + + protected static IndexVersion getOldClusterIndexVersion() { + assert oldIndexVersion != null; + return oldIndexVersion; + } + + protected static Version getOldClusterTestVersion() { + return Version.fromString(OLD_CLUSTER_VERSION.toString()); + } + + protected static boolean isOldCluster() { + return upgradedNodes.isEmpty(); + } + + protected static boolean isFirstMixedCluster() { + return upgradedNodes.size() == 1; + } + + protected static boolean isMixedCluster() { + return upgradedNodes.isEmpty() == false && upgradedNodes.size() < NODE_NUM; + } + + protected static boolean isUpgradedCluster() { + return upgradedNodes.size() == NODE_NUM; + } + + @Override + protected String getTestRestCluster() { + return cluster.getHttpAddresses(); + } + + @Override + protected final boolean resetFeatureStates() { + return false; + } + + @Override + protected final boolean preserveIndicesUponCompletion() { + return true; + } + + @Override + protected final boolean preserveDataStreamsUponCompletion() { + return true; + } + + @Override + protected final boolean preserveReposUponCompletion() { + return true; + } + + @Override + protected boolean preserveTemplatesUponCompletion() { + return true; + } + + @Override + protected boolean preserveClusterUponCompletion() { + return true; + } + + @Override + protected final Settings restClientSettings() { + return Settings.builder() + .put(super.restClientSettings()) + // increase the timeout here to 90 seconds to handle long waits for a green + // cluster health. the waits for green need to be longer than a minute to + // account for delayed shards + .put(ESRestTestCase.CLIENT_SOCKET_TIMEOUT, "90s") + .build(); + } + + @Override + protected final String getEnsureGreenTimeout() { + // increase the timeout here to 70 seconds to handle long waits for a green + // cluster health. the waits for green need to be longer than a minute to + // account for delayed shards + return "70s"; + } +} diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java similarity index 61% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java index b8ce7fe7a6fbb..95f4c55314199 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.util.EntityUtils; @@ -40,100 +42,102 @@ import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; -public class SnapshotBasedRecoveryIT extends AbstractRollingTestCase { +public class SnapshotBasedRecoveryIT extends ParameterizedRollingUpgradeTestCase { + + public SnapshotBasedRecoveryIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } public void testSnapshotBasedRecovery() throws Exception { assumeFalse( "Cancel shard allocation command is broken for initial desired balance versions and might allocate shard " + "on the node where it is not supposed to be. Fixed by https://github.com/elastic/elasticsearch/pull/93635", - UPGRADE_FROM_VERSION == Version.V_8_6_0 || UPGRADE_FROM_VERSION == Version.V_8_6_1 || UPGRADE_FROM_VERSION == Version.V_8_7_0 + getOldClusterVersion() == Version.V_8_6_0 + || getOldClusterVersion() == Version.V_8_6_1 + || getOldClusterVersion() == Version.V_8_7_0 ); final String indexName = "snapshot_based_recovery"; final String repositoryName = "snapshot_based_recovery_repo"; final int numDocs = 200; - switch (CLUSTER_TYPE) { - case OLD -> { - Settings.Builder settings = Settings.builder() - .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) - .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) - .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") - .put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster - createIndex(indexName, settings.build()); - ensureGreen(indexName); - indexDocs(indexName, numDocs); - flush(indexName, true); - registerRepository( - repositoryName, - "fs", - true, - Settings.builder() - .put("location", "./snapshot_based_recovery") - .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), true) - .build() - ); - createSnapshot(repositoryName, "snap", true); - updateIndexSettings(indexName, Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)); - ensureGreen(indexName); - } - case MIXED, UPGRADED -> { - if (FIRST_MIXED_ROUND) { - List upgradedNodeIds = getUpgradedNodeIds(); - // It's possible that the test simply does a rolling-restart, i.e. it "upgrades" to - // the same version. In that case we proceed without excluding any node - if (upgradedNodeIds.isEmpty() == false) { - assertThat(upgradedNodeIds.size(), is(equalTo(1))); - String upgradedNodeId = upgradedNodeIds.get(0); - logger.info("--> excluding [{}] from node [{}]", indexName, upgradedNodeId); - updateIndexSettings(indexName, Settings.builder().put("index.routing.allocation.exclude._id", upgradedNodeId)); - ensureGreen(indexName); - logger.info("--> finished excluding [{}] from node [{}]", indexName, upgradedNodeId); - } else { - logger.info("--> no upgrading nodes, not adding any exclusions for [{}]", indexName); - } + if (isOldCluster()) { + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") + .put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster + createIndex(indexName, settings.build()); + ensureGreen(indexName); + indexDocs(indexName, numDocs); + flush(indexName, true); + registerRepository( + repositoryName, + "fs", + true, + Settings.builder() + .put("location", "./snapshot_based_recovery") + .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), true) + .build() + ); + createSnapshot(repositoryName, "snap", true); + updateIndexSettings(indexName, Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)); + ensureGreen(indexName); + } else { + if (isFirstMixedCluster()) { + List upgradedNodeIds = getUpgradedNodeIds(); + // It's possible that the test simply does a rolling-restart, i.e. it "upgrades" to + // the same version. In that case we proceed without excluding any node + if (upgradedNodeIds.isEmpty() == false) { + assertThat(upgradedNodeIds.size(), is(equalTo(1))); + String upgradedNodeId = upgradedNodeIds.get(0); + logger.info("--> excluding [{}] from node [{}]", indexName, upgradedNodeId); + updateIndexSettings(indexName, Settings.builder().put("index.routing.allocation.exclude._id", upgradedNodeId)); + ensureGreen(indexName); + logger.info("--> finished excluding [{}] from node [{}]", indexName, upgradedNodeId); + } else { + logger.info("--> no upgrading nodes, not adding any exclusions for [{}]", indexName); + } - String primaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - Version primaryNodeVersion = getNodeVersion(primaryNodeId); + String primaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); + Version primaryNodeVersion = getNodeVersion(primaryNodeId); - // Sometimes the primary shard ends on the upgraded node (i.e. after a rebalance) - // This causes issues when removing and adding replicas, since then we cannot allocate to any of the old nodes. - // That is an issue only for the first mixed round. - // In that case we exclude the upgraded node from the shard allocation and cancel the shard to force moving - // the primary to a node in the old version, this allows adding replicas in the first mixed round. - logger.info("--> Primary node in first mixed round {} / {}", primaryNodeId, primaryNodeVersion); - if (primaryNodeVersion.after(UPGRADE_FROM_VERSION)) { - logger.info("--> cancelling primary shard on node [{}]", primaryNodeId); - cancelShard(indexName, 0, primaryNodeId); - logger.info("--> done cancelling primary shard on node [{}]", primaryNodeId); + // Sometimes the primary shard ends on the upgraded node (i.e. after a rebalance) + // This causes issues when removing and adding replicas, since then we cannot allocate to any of the old nodes. + // That is an issue only for the first mixed round. + // In that case we exclude the upgraded node from the shard allocation and cancel the shard to force moving + // the primary to a node in the old version, this allows adding replicas in the first mixed round. + logger.info("--> Primary node in first mixed round {} / {}", primaryNodeId, primaryNodeVersion); + if (primaryNodeVersion.after(getOldClusterVersion())) { + logger.info("--> cancelling primary shard on node [{}]", primaryNodeId); + cancelShard(indexName, 0, primaryNodeId); + logger.info("--> done cancelling primary shard on node [{}]", primaryNodeId); - String currentPrimaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - assertThat(getNodeVersion(currentPrimaryNodeId), is(equalTo(UPGRADE_FROM_VERSION))); - } - } else { - logger.info("--> not in first upgrade round, removing exclusions for [{}]", indexName); - updateIndexSettings(indexName, Settings.builder().putNull("index.routing.allocation.exclude._id")); - logger.info("--> done removing exclusions for [{}]", indexName); + String currentPrimaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); + assertThat(getNodeVersion(currentPrimaryNodeId), is(equalTo(getOldClusterVersion()))); } + } else { + logger.info("--> not in first upgrade round, removing exclusions for [{}]", indexName); + updateIndexSettings(indexName, Settings.builder().putNull("index.routing.allocation.exclude._id")); + logger.info("--> done removing exclusions for [{}]", indexName); + } - // Drop replicas - logger.info("--> dropping replicas from [{}]", indexName); - updateIndexSettingsPermittingSlowlogDeprecationWarning( - indexName, - Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) - ); - logger.info("--> finished dropping replicas from [{}], adding them back", indexName); - updateIndexSettingsPermittingSlowlogDeprecationWarning( - indexName, - Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) - ); - logger.info("--> finished adding replicas from [{}]", indexName); - ensureGreen(indexName); + // Drop replicas + logger.info("--> dropping replicas from [{}]", indexName); + updateIndexSettingsPermittingSlowlogDeprecationWarning( + indexName, + Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + ); + logger.info("--> finished dropping replicas from [{}], adding them back", indexName); + updateIndexSettingsPermittingSlowlogDeprecationWarning( + indexName, + Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) + ); + logger.info("--> finished adding replicas from [{}]", indexName); + ensureGreen(indexName); - assertMatchAllReturnsAllDocuments(indexName, numDocs); - assertMatchQueryReturnsAllDocuments(indexName, numDocs); - } - default -> throw new IllegalStateException("unknown type " + CLUSTER_TYPE); + assertMatchAllReturnsAllDocuments(indexName, numDocs); + assertMatchQueryReturnsAllDocuments(indexName, numDocs); } } @@ -145,7 +149,7 @@ private List getUpgradedNodeIds() throws IOException { List upgradedNodes = new ArrayList<>(); for (Map.Entry> nodeInfoEntry : nodes.entrySet()) { Version nodeVersion = Version.fromString(extractValue(nodeInfoEntry.getValue(), "version")); - if (nodeVersion.after(UPGRADE_FROM_VERSION)) { + if (nodeVersion.after(getOldClusterVersion())) { upgradedNodes.add(nodeInfoEntry.getKey()); } } diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java similarity index 95% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java index 48c3007bb2674..44ee7f0b56d1c 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SystemIndicesUpgradeIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; import org.elasticsearch.index.IndexVersion; @@ -21,13 +23,17 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -public class SystemIndicesUpgradeIT extends AbstractRollingTestCase { +public class SystemIndicesUpgradeIT extends ParameterizedRollingUpgradeTestCase { + + public SystemIndicesUpgradeIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } @SuppressWarnings("unchecked") public void testSystemIndicesUpgrades() throws Exception { final String systemIndexWarning = "this request accesses system indices: [.tasks], but in a future major version, direct " + "access to system indices will be prevented by default"; - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { // create index Request createTestIndex = new Request("PUT", "/test_index_old"); createTestIndex.setJsonEntity("{\"settings\": {\"index.number_of_replicas\": 0}}"); @@ -99,7 +105,7 @@ public void testSystemIndicesUpgrades() throws Exception { })); assertThat(client().performRequest(putAliasRequest).getStatusLine().getStatusCode(), is(200)); } - } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { + } else if (isUpgradedCluster()) { assertBusy(() -> { Request clusterStateRequest = new Request("GET", "/_cluster/state/metadata"); Map indices = new JsonMapView(entityAsMap(client().performRequest(clusterStateRequest))).get( diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java similarity index 90% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java index 19f24c97a47f8..b42646164b335 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TsdbIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TsdbIT.java @@ -8,6 +8,8 @@ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.common.time.DateFormatter; @@ -24,7 +26,11 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class TsdbIT extends AbstractRollingTestCase { +public class TsdbIT extends ParameterizedRollingUpgradeTestCase { + + public TsdbIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } private static final String TEMPLATE = """ { @@ -88,21 +94,21 @@ public class TsdbIT extends AbstractRollingTestCase { private static final String BULK = """ {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959507","ip": "10.10.55.1", "network": {"tx": 2001818691, "rx": 802133794}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959507", "ip": "10.10.55.1", "network": {"tx": 2001818691, "rx": 802133794}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "hamster", "uid":"947e4ced-1786-4e53-9e0c-5c447e959508","ip": "10.10.55.1", "network": {"tx": 2005177954, "rx": 801479970}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "hamster", "uid":"947e4ced-1786-4e53-9e0c-5c447e959508", "ip": "10.10.55.1", "network": {"tx": 2005177954, "rx": 801479970}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cow", "uid":"947e4ced-1786-4e53-9e0c-5c447e959509","ip": "10.10.55.1", "network": {"tx": 2006223737, "rx": 802337279}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "cow", "uid":"947e4ced-1786-4e53-9e0c-5c447e959509", "ip": "10.10.55.1", "network": {"tx": 2006223737, "rx": 802337279}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "rat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959510","ip": "10.10.55.2", "network": {"tx": 2012916202, "rx": 803685721}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "rat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959510", "ip": "10.10.55.2", "network": {"tx": 2012916202, "rx": 803685721}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "dog", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9","ip": "10.10.55.3", "network": {"tx": 1434521831, "rx": 530575198}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "dog", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9", "ip": "10.10.55.3", "network": {"tx": 1434521831, "rx": 530575198}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "tiger", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea10","ip": "10.10.55.3", "network": {"tx": 1434577921, "rx": 530600088}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "tiger", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea10", "ip": "10.10.55.3", "network": {"tx": 1434577921, "rx": 530600088}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "lion", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876e11","ip": "10.10.55.3", "network": {"tx": 1434587694, "rx": 530604797}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "lion", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876e11", "ip": "10.10.55.3", "network": {"tx": 1434587694, "rx": 530604797}}}} {"create": {}} - {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "elephant", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876eb4","ip": "10.10.55.3", "network": {"tx": 1434595272, "rx": 530605511}}}} + {"@timestamp": "$now", "metricset": "pod", "k8s": {"pod": {"name": "elephant", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876eb4", "ip": "10.10.55.3", "network": {"tx": 1434595272, "rx": 530605511}}}} """; private static final String DOC = """ @@ -125,11 +131,11 @@ public class TsdbIT extends AbstractRollingTestCase { public void testTsdbDataStream() throws Exception { assumeTrue( - "Skipping version [" + UPGRADE_FROM_VERSION + "], because TSDB was GA-ed in 8.7.0", - UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_7_0) + "Skipping version [" + getOldClusterVersion() + "], because TSDB was GA-ed in 8.7.0", + getOldClusterVersion().onOrAfter(Version.V_8_7_0) ); String dataStreamName = "k8s"; - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { final String INDEX_TEMPLATE = """ { "index_patterns": ["$PATTERN"], @@ -144,20 +150,20 @@ public void testTsdbDataStream() throws Exception { assertOK(client().performRequest(putIndexTemplateRequest)); performOldClustertOperations(templateName, dataStreamName); - } else if (CLUSTER_TYPE == ClusterType.MIXED) { + } else if (isMixedCluster()) { performMixedClusterOperations(dataStreamName); - } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { + } else if (isUpgradedCluster()) { performUpgradedClusterOperations(dataStreamName); } } public void testTsdbDataStreamWithComponentTemplate() throws Exception { assumeTrue( - "Skipping version [" + UPGRADE_FROM_VERSION + "], because TSDB was GA-ed in 8.7.0 and bug was fixed in 8.11.0", - UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_7_0) && UPGRADE_FROM_VERSION.before(Version.V_8_11_0) + "Skipping version [" + getOldClusterVersion() + "], because TSDB was GA-ed in 8.7.0 and bug was fixed in 8.11.0", + getOldClusterVersion().onOrAfter(Version.V_8_7_0) && getOldClusterVersion().before(Version.V_8_11_0) ); String dataStreamName = "template-with-component-template"; - if (CLUSTER_TYPE == ClusterType.OLD) { + if (isOldCluster()) { final String COMPONENT_TEMPLATE = """ { "template": $TEMPLATE @@ -181,9 +187,9 @@ public void testTsdbDataStreamWithComponentTemplate() throws Exception { assertOK(client().performRequest(putIndexTemplateRequest)); performOldClustertOperations(templateName, dataStreamName); - } else if (CLUSTER_TYPE == ClusterType.MIXED) { + } else if (isMixedCluster()) { performMixedClusterOperations(dataStreamName); - } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { + } else if (isUpgradedCluster()) { performUpgradedClusterOperations(dataStreamName); var dataStreams = getDataStream(dataStreamName); @@ -242,7 +248,7 @@ private void performUpgradedClusterOperations(String dataStreamName) throws Exce private static void performMixedClusterOperations(String dataStreamName) throws IOException { ensureHealth(dataStreamName, request -> request.addParameter("wait_for_status", "yellow")); - if (FIRST_MIXED_ROUND) { + if (isFirstMixedCluster()) { indexDoc(dataStreamName); } assertSearch(dataStreamName, 9); diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java new file mode 100644 index 0000000000000..9647bfb739164 --- /dev/null +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.upgrades; + +import com.carrotsearch.randomizedtesting.annotations.Name; + +import org.elasticsearch.Version; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.core.Strings; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; +import static org.hamcrest.Matchers.is; + +public class UpgradeWithOldIndexSettingsIT extends ParameterizedRollingUpgradeTestCase { + + public UpgradeWithOldIndexSettingsIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } + + private static final String INDEX_NAME = "test_index_old_settings"; + private static final String EXPECTED_WARNING = "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will " + + "be removed in a future release! See the breaking changes documentation for the next major version."; + + private static final String EXPECTED_V8_WARNING = "[index.indexing.slowlog.level] setting was deprecated in the previous Elasticsearch" + + " release and is removed in this release."; + + public void testOldIndexSettings() throws Exception { + if (isOldCluster()) { + Request createTestIndex = new Request("PUT", "/" + INDEX_NAME); + createTestIndex.setJsonEntity("{\"settings\": {\"index.indexing.slowlog.level\": \"WARN\"}}"); + createTestIndex.setOptions(expectWarnings(EXPECTED_WARNING)); + if (getOldClusterVersion().before(Version.V_8_0_0)) { + // create index with settings no longer valid in 8.0 + client().performRequest(createTestIndex); + } else { + assertTrue( + expectThrows(ResponseException.class, () -> client().performRequest(createTestIndex)).getMessage() + .contains("unknown setting [index.indexing.slowlog.level]") + ); + + Request createTestIndex1 = new Request("PUT", "/" + INDEX_NAME); + client().performRequest(createTestIndex1); + } + + // add some data + Request bulk = new Request("POST", "/_bulk"); + bulk.addParameter("refresh", "true"); + if (getOldClusterVersion().before(Version.V_8_0_0)) { + bulk.setOptions(expectWarnings(EXPECTED_WARNING)); + } + bulk.setJsonEntity(Strings.format(""" + {"index": {"_index": "%s"}} + {"f1": "v1", "f2": "v2"} + """, INDEX_NAME)); + client().performRequest(bulk); + } else if (isMixedCluster()) { + // add some more data + Request bulk = new Request("POST", "/_bulk"); + bulk.addParameter("refresh", "true"); + if (getOldClusterVersion().before(Version.V_8_0_0)) { + bulk.setOptions(expectWarnings(EXPECTED_WARNING)); + } + bulk.setJsonEntity(Strings.format(""" + {"index": {"_index": "%s"}} + {"f1": "v3", "f2": "v4"} + """, INDEX_NAME)); + client().performRequest(bulk); + } else { + if (getOldClusterVersion().before(Version.V_8_0_0)) { + Request createTestIndex = new Request("PUT", "/" + INDEX_NAME + "/_settings"); + // update index settings should work + createTestIndex.setJsonEntity("{\"index.indexing.slowlog.level\": \"INFO\"}"); + createTestIndex.setOptions(expectWarnings(EXPECTED_V8_WARNING)); + client().performRequest(createTestIndex); + + // ensure we were able to change the setting, despite it having no effect + Request indexSettingsRequest = new Request("GET", "/" + INDEX_NAME + "/_settings"); + Map response = entityAsMap(client().performRequest(indexSettingsRequest)); + + var slowLogLevel = (String) (XContentMapValues.extractValue( + INDEX_NAME + ".settings.index.indexing.slowlog.level", + response + )); + + // check that we can read our old index settings + assertThat(slowLogLevel, is("INFO")); + } + assertCount(INDEX_NAME, 2); + } + } + + private void assertCount(String index, int countAtLeast) throws IOException { + Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); + searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); + searchTestIndexRequest.addParameter("filter_path", "hits.total"); + Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); + Map response = entityAsMap(searchTestIndexResponse); + + var hitsTotal = (Integer) (XContentMapValues.extractValue("hits.total", response)); + + assertTrue(hitsTotal >= countAtLeast); + } + + public static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { + Request request = new Request("PUT", "/" + index + "/_settings"); + request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); + if (getOldClusterVersion().before(Version.V_7_17_9)) { + // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) + // Below warnings are set (and leaking) from an index in this test case + request.setOptions(expectVersionSpecificWarnings(v -> { + v.compatible( + "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " + + "See the breaking changes documentation for the next major version." + ); + })); + } + client().performRequest(request); + } +} diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java similarity index 93% rename from qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java rename to qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java index 40e63b4ae32d7..dade5b53addae 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/XPackIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/XPackIT.java @@ -7,6 +7,8 @@ */ package org.elasticsearch.upgrades; +import com.carrotsearch.randomizedtesting.annotations.Name; + import org.apache.http.util.EntityUtils; import org.elasticsearch.client.Request; import org.junit.Before; @@ -20,7 +22,12 @@ * Basic tests for simple xpack functionality that are only run if the * cluster is the on the default distribution. */ -public class XPackIT extends AbstractRollingTestCase { +public class XPackIT extends ParameterizedRollingUpgradeTestCase { + + public XPackIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } + @Before public void skipIfNotXPack() { assumeThat( @@ -28,10 +35,9 @@ public void skipIfNotXPack() { System.getProperty("tests.distribution"), equalTo("default") ); - assumeThat( + assumeTrue( "running this on the unupgraded cluster would change its state and it wouldn't work prior to 6.3 anyway", - CLUSTER_TYPE, - equalTo(ClusterType.UPGRADED) + isUpgradedCluster() ); /* * *Mostly* we want this for when we're upgrading from pre-6.3's diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java deleted file mode 100644 index 35688e7c244cf..0000000000000 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeWithOldIndexSettingsIT.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.upgrades; - -import org.elasticsearch.Version; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.ResponseException; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.support.XContentMapValues; -import org.elasticsearch.core.Strings; - -import java.io.IOException; -import java.util.Map; - -import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; -import static org.hamcrest.Matchers.is; - -public class UpgradeWithOldIndexSettingsIT extends AbstractRollingTestCase { - - private static final String INDEX_NAME = "test_index_old_settings"; - private static final String EXPECTED_WARNING = "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will " - + "be removed in a future release! See the breaking changes documentation for the next major version."; - - private static final String EXPECTED_V8_WARNING = "[index.indexing.slowlog.level] setting was deprecated in the previous Elasticsearch" - + " release and is removed in this release."; - - @SuppressWarnings("unchecked") - public void testOldIndexSettings() throws Exception { - switch (CLUSTER_TYPE) { - case OLD -> { - Request createTestIndex = new Request("PUT", "/" + INDEX_NAME); - createTestIndex.setJsonEntity("{\"settings\": {\"index.indexing.slowlog.level\": \"WARN\"}}"); - createTestIndex.setOptions(expectWarnings(EXPECTED_WARNING)); - if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { - // create index with settings no longer valid in 8.0 - client().performRequest(createTestIndex); - } else { - assertTrue( - expectThrows(ResponseException.class, () -> client().performRequest(createTestIndex)).getMessage() - .contains("unknown setting [index.indexing.slowlog.level]") - ); - - Request createTestIndex1 = new Request("PUT", "/" + INDEX_NAME); - client().performRequest(createTestIndex1); - } - - // add some data - Request bulk = new Request("POST", "/_bulk"); - bulk.addParameter("refresh", "true"); - if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { - bulk.setOptions(expectWarnings(EXPECTED_WARNING)); - } - bulk.setJsonEntity(Strings.format(""" - {"index": {"_index": "%s"}} - {"f1": "v1", "f2": "v2"} - """, INDEX_NAME)); - client().performRequest(bulk); - } - case MIXED -> { - // add some more data - Request bulk = new Request("POST", "/_bulk"); - bulk.addParameter("refresh", "true"); - if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { - bulk.setOptions(expectWarnings(EXPECTED_WARNING)); - } - bulk.setJsonEntity(Strings.format(""" - {"index": {"_index": "%s"}} - {"f1": "v3", "f2": "v4"} - """, INDEX_NAME)); - client().performRequest(bulk); - } - case UPGRADED -> { - if (UPGRADE_FROM_VERSION.before(Version.V_8_0_0)) { - Request createTestIndex = new Request("PUT", "/" + INDEX_NAME + "/_settings"); - // update index settings should work - createTestIndex.setJsonEntity("{\"index.indexing.slowlog.level\": \"INFO\"}"); - createTestIndex.setOptions(expectWarnings(EXPECTED_V8_WARNING)); - client().performRequest(createTestIndex); - - // ensure we were able to change the setting, despite it having no effect - Request indexSettingsRequest = new Request("GET", "/" + INDEX_NAME + "/_settings"); - Map response = entityAsMap(client().performRequest(indexSettingsRequest)); - - var slowLogLevel = (String) (XContentMapValues.extractValue( - INDEX_NAME + ".settings.index.indexing.slowlog.level", - response - )); - - // check that we can read our old index settings - assertThat(slowLogLevel, is("INFO")); - } - assertCount(INDEX_NAME, 2); - } - } - } - - private void assertCount(String index, int countAtLeast) throws IOException { - Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); - searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); - searchTestIndexRequest.addParameter("filter_path", "hits.total"); - Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); - Map response = entityAsMap(searchTestIndexResponse); - - var hitsTotal = (Integer) (XContentMapValues.extractValue("hits.total", response)); - - assertTrue(hitsTotal >= countAtLeast); - } - - public static void updateIndexSettingsPermittingSlowlogDeprecationWarning(String index, Settings.Builder settings) throws IOException { - Request request = new Request("PUT", "/" + index + "/_settings"); - request.setJsonEntity(org.elasticsearch.common.Strings.toString(settings.build())); - if (UPGRADE_FROM_VERSION.before(Version.V_7_17_9)) { - // There is a bug (fixed in 7.17.9 and 8.7.0 where deprecation warnings could leak into ClusterApplierService#applyChanges) - // Below warnings are set (and leaking) from an index in this test case - request.setOptions(expectVersionSpecificWarnings(v -> { - v.compatible( - "[index.indexing.slowlog.level] setting was deprecated in Elasticsearch and will be removed in a future release! " - + "See the breaking changes documentation for the next major version." - ); - })); - } - client().performRequest(request); - } -} From 5e3ab06151ded1c134499862a2a1d89da169d181 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 22 Sep 2023 11:27:13 -0400 Subject: [PATCH 042/155] ESQL: Prevent `CONCAT` from using a ton of memory (#99716) This prevents `CONCAT` from using an unbounded amount of memory by hooking it's temporary value into the circuit breaker. To do so, it makes *all* `ExpressionEvaluator`s `Releasable`. Most of the changes in this PR just plumb that through to every evaluator. The rest of the changes correctly release evaluators after their use. I considered another tactic but didn't like it as much, even though the number of changes would be smaller - I could have created a fresh, `Releasable` temporary value for every `Page`. It would be pretty contained keep the releasable there. But I wanted to share the temporary state across runs to avoid a bunch of allocations. Here's a script that used to crash before this PR but is fine after: ``` curl -uelastic:password -XDELETE localhost:9200/test curl -HContent-Type:application/json -uelastic:password -XPUT localhost:9200/test -d'{ "mappings": { "properties": { "short": { "type": "keyword" } } } }' curl -HContent-Type:application/json -uelastic:password -XPUT localhost:9200/test/_doc/1?refresh -d'{"short": "short"}' echo -n '{"query": "FROM test ' > /tmp/evil for i in {0..9}; do echo -n '| EVAL short = CONCAT(short' >> /tmp/evil for j in {1..9}; do echo -n ', short' >> /tmp/evil done echo -n ')' >> /tmp/evil done echo '| EVAL len = LENGTH(short) | KEEP len"}'>> /tmp/evil curl -HContent-Type:application/json -uelastic:password -XPOST localhost:9200/_query?pretty --data-binary @/tmp/evil ``` --- .../esql/functions/signature/to_ip.svg | 1 + .../esql/functions/types/concat.asciidoc | 1 + .../esql/functions/types/to_ip.asciidoc | 6 + .../compute/gen/EvaluatorImplementer.java | 73 +++++++- .../compute/gen/EvaluatorProcessor.java | 1 + .../org/elasticsearch/compute/gen/Types.java | 18 ++ .../operator/AbstractPageMappingOperator.java | 2 +- .../operator/ColumnExtractOperator.java | 5 + .../compute/operator/DriverContext.java | 8 + .../compute/operator/EvalOperator.java | 18 +- .../compute/operator/FilterOperator.java | 6 + .../compute/operator/MultivalueDedupe.java | 3 + .../operator/StringExtractOperator.java | 6 +- .../compute/operator/topn/TopNOperator.java | 9 +- .../operator/ColumnExtractOperatorTests.java | 14 +- .../compute/operator/EvalOperatorTests.java | 3 + .../compute/operator/FilterOperatorTests.java | 3 + .../operator/StringExtractOperatorTests.java | 28 ++- .../comparison/EqualsBoolsEvaluator.java | 6 + .../comparison/EqualsDoublesEvaluator.java | 6 + .../comparison/EqualsIntsEvaluator.java | 6 + .../comparison/EqualsKeywordsEvaluator.java | 6 + .../comparison/EqualsLongsEvaluator.java | 6 + .../GreaterThanDoublesEvaluator.java | 6 + .../comparison/GreaterThanIntsEvaluator.java | 6 + .../GreaterThanKeywordsEvaluator.java | 6 + .../comparison/GreaterThanLongsEvaluator.java | 6 + .../GreaterThanOrEqualDoublesEvaluator.java | 6 + .../GreaterThanOrEqualIntsEvaluator.java | 6 + .../GreaterThanOrEqualKeywordsEvaluator.java | 6 + .../GreaterThanOrEqualLongsEvaluator.java | 6 + .../comparison/LessThanDoublesEvaluator.java | 6 + .../comparison/LessThanIntsEvaluator.java | 6 + .../comparison/LessThanKeywordsEvaluator.java | 6 + .../comparison/LessThanLongsEvaluator.java | 6 + .../LessThanOrEqualDoublesEvaluator.java | 6 + .../LessThanOrEqualIntsEvaluator.java | 6 + .../LessThanOrEqualKeywordsEvaluator.java | 6 + .../LessThanOrEqualLongsEvaluator.java | 6 + .../comparison/NotEqualsBoolsEvaluator.java | 6 + .../comparison/NotEqualsDoublesEvaluator.java | 6 + .../comparison/NotEqualsIntsEvaluator.java | 6 + .../NotEqualsKeywordsEvaluator.java | 6 + .../comparison/NotEqualsLongsEvaluator.java | 6 + .../operator/logical/NotEvaluator.java | 6 + .../operator/regex/RegexMatchEvaluator.java | 6 + .../conditional/GreatestBooleanEvaluator.java | 6 + .../GreatestBytesRefEvaluator.java | 6 + .../conditional/GreatestDoubleEvaluator.java | 6 + .../conditional/GreatestIntEvaluator.java | 6 + .../conditional/GreatestLongEvaluator.java | 6 + .../conditional/LeastBooleanEvaluator.java | 6 + .../conditional/LeastBytesRefEvaluator.java | 6 + .../conditional/LeastDoubleEvaluator.java | 6 + .../scalar/conditional/LeastIntEvaluator.java | 6 + .../conditional/LeastLongEvaluator.java | 6 + .../date/DateExtractConstantEvaluator.java | 6 + .../scalar/date/DateExtractEvaluator.java | 6 + .../date/DateFormatConstantEvaluator.java | 6 + .../scalar/date/DateFormatEvaluator.java | 6 + .../date/DateParseConstantEvaluator.java | 6 + .../scalar/date/DateParseEvaluator.java | 6 + .../scalar/date/DateTruncEvaluator.java | 6 + .../function/scalar/date/NowEvaluator.java | 4 + .../scalar/ip/CIDRMatchEvaluator.java | 6 + .../scalar/math/AbsDoubleEvaluator.java | 6 + .../function/scalar/math/AbsIntEvaluator.java | 6 + .../scalar/math/AbsLongEvaluator.java | 6 + .../function/scalar/math/AcosEvaluator.java | 6 + .../function/scalar/math/AsinEvaluator.java | 6 + .../function/scalar/math/Atan2Evaluator.java | 6 + .../function/scalar/math/AtanEvaluator.java | 6 + .../scalar/math/CastIntToDoubleEvaluator.java | 6 + .../scalar/math/CastIntToLongEvaluator.java | 6 + .../math/CastIntToUnsignedLongEvaluator.java | 6 + .../math/CastLongToDoubleEvaluator.java | 6 + .../math/CastLongToUnsignedLongEvaluator.java | 6 + .../CastUnsignedLongToDoubleEvaluator.java | 6 + .../scalar/math/CeilDoubleEvaluator.java | 6 + .../function/scalar/math/CosEvaluator.java | 6 + .../function/scalar/math/CoshEvaluator.java | 6 + .../scalar/math/FloorDoubleEvaluator.java | 6 + .../scalar/math/IsFiniteEvaluator.java | 6 + .../scalar/math/IsInfiniteEvaluator.java | 6 + .../function/scalar/math/IsNaNEvaluator.java | 6 + .../scalar/math/Log10DoubleEvaluator.java | 6 + .../scalar/math/Log10IntEvaluator.java | 6 + .../scalar/math/Log10LongEvaluator.java | 6 + .../math/Log10UnsignedLongEvaluator.java | 6 + .../scalar/math/PowDoubleEvaluator.java | 6 + .../function/scalar/math/PowIntEvaluator.java | 6 + .../scalar/math/PowLongEvaluator.java | 6 + .../scalar/math/RoundDoubleEvaluator.java | 6 + .../math/RoundDoubleNoDecimalsEvaluator.java | 6 + .../scalar/math/RoundIntEvaluator.java | 6 + .../scalar/math/RoundLongEvaluator.java | 6 + .../math/RoundUnsignedLongEvaluator.java | 6 + .../function/scalar/math/SinEvaluator.java | 6 + .../function/scalar/math/SinhEvaluator.java | 6 + .../scalar/math/SqrtDoubleEvaluator.java | 6 + .../scalar/math/SqrtIntEvaluator.java | 6 + .../scalar/math/SqrtLongEvaluator.java | 6 + .../math/SqrtUnsignedLongEvaluator.java | 6 + .../function/scalar/math/TanEvaluator.java | 6 + .../function/scalar/math/TanhEvaluator.java | 6 + .../scalar/string/ConcatEvaluator.java | 12 +- .../scalar/string/EndsWithEvaluator.java | 6 + .../scalar/string/LTrimEvaluator.java | 6 + .../function/scalar/string/LeftEvaluator.java | 6 + .../scalar/string/LengthEvaluator.java | 6 + .../scalar/string/RTrimEvaluator.java | 6 + .../scalar/string/RightEvaluator.java | 6 + .../string/SplitSingleByteEvaluator.java | 6 + .../scalar/string/SplitVariableEvaluator.java | 6 + .../scalar/string/StartsWithEvaluator.java | 6 + .../scalar/string/SubstringEvaluator.java | 6 + .../string/SubstringNoLengthEvaluator.java | 6 + .../function/scalar/string/TrimEvaluator.java | 6 + .../arithmetic/AddDatetimesEvaluator.java | 6 + .../arithmetic/AddDoublesEvaluator.java | 6 + .../operator/arithmetic/AddIntsEvaluator.java | 6 + .../arithmetic/AddLongsEvaluator.java | 6 + .../arithmetic/AddUnsignedLongsEvaluator.java | 6 + .../arithmetic/DivDoublesEvaluator.java | 6 + .../operator/arithmetic/DivIntsEvaluator.java | 6 + .../arithmetic/DivLongsEvaluator.java | 6 + .../arithmetic/DivUnsignedLongsEvaluator.java | 6 + .../arithmetic/ModDoublesEvaluator.java | 6 + .../operator/arithmetic/ModIntsEvaluator.java | 6 + .../arithmetic/ModLongsEvaluator.java | 6 + .../arithmetic/ModUnsignedLongsEvaluator.java | 6 + .../arithmetic/MulDoublesEvaluator.java | 6 + .../operator/arithmetic/MulIntsEvaluator.java | 6 + .../arithmetic/MulLongsEvaluator.java | 6 + .../arithmetic/MulUnsignedLongsEvaluator.java | 6 + .../arithmetic/NegDoublesEvaluator.java | 6 + .../operator/arithmetic/NegIntsEvaluator.java | 6 + .../arithmetic/NegLongsEvaluator.java | 6 + .../arithmetic/SubDatetimesEvaluator.java | 6 + .../arithmetic/SubDoublesEvaluator.java | 6 + .../operator/arithmetic/SubIntsEvaluator.java | 6 + .../arithmetic/SubLongsEvaluator.java | 6 + .../arithmetic/SubUnsignedLongsEvaluator.java | 6 + .../xpack/esql/evaluator/EvalMapper.java | 21 +++ .../evaluator/mapper/EvaluatorMapper.java | 22 ++- .../operator/comparison/InMapper.java | 6 + .../function/scalar/conditional/Case.java | 14 +- .../convert/AbstractConvertFunction.java | 7 + .../expression/function/scalar/math/Cast.java | 4 +- .../AbstractMultivalueFunction.java | 6 + .../function/scalar/multivalue/MvConcat.java | 5 +- .../function/scalar/nulls/Coalesce.java | 6 + .../function/scalar/string/Concat.java | 8 +- .../xpack/esql/plugin/ComputeService.java | 4 +- .../elasticsearch/xpack/esql/CsvTests.java | 2 +- .../function/AbstractFunctionTestCase.java | 80 +++++---- .../scalar/conditional/CaseTests.java | 13 +- .../function/scalar/nulls/CoalesceTests.java | 12 +- .../function/scalar/string/ConcatTests.java | 159 +++++++++--------- 159 files changed, 1170 insertions(+), 158 deletions(-) create mode 100644 docs/reference/esql/functions/signature/to_ip.svg create mode 100644 docs/reference/esql/functions/types/to_ip.asciidoc diff --git a/docs/reference/esql/functions/signature/to_ip.svg b/docs/reference/esql/functions/signature/to_ip.svg new file mode 100644 index 0000000000000..c049964b254f3 --- /dev/null +++ b/docs/reference/esql/functions/signature/to_ip.svg @@ -0,0 +1 @@ +TO_IP(arg1) \ No newline at end of file diff --git a/docs/reference/esql/functions/types/concat.asciidoc b/docs/reference/esql/functions/types/concat.asciidoc index 0a34dcd64f1ee..67aed9286f06f 100644 --- a/docs/reference/esql/functions/types/concat.asciidoc +++ b/docs/reference/esql/functions/types/concat.asciidoc @@ -2,4 +2,5 @@ |=== arg1 | arg2... | result keyword | keyword | keyword +text | text | keyword |=== diff --git a/docs/reference/esql/functions/types/to_ip.asciidoc b/docs/reference/esql/functions/types/to_ip.asciidoc new file mode 100644 index 0000000000000..a21bbf14d87ca --- /dev/null +++ b/docs/reference/esql/functions/types/to_ip.asciidoc @@ -0,0 +1,6 @@ +[%header.monospaced.styled,format=dsv,separator=|] +|=== +arg1 | result +ip | ip +keyword | ip +|=== diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java index 92601a232a68b..ecd59edb03286 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java @@ -47,9 +47,15 @@ public class EvaluatorImplementer { private final ProcessFunction processFunction; private final ClassName implementation; - public EvaluatorImplementer(Elements elements, ExecutableElement processFunction, String extraName, List warnExceptions) { + public EvaluatorImplementer( + Elements elements, + javax.lang.model.util.Types types, + ExecutableElement processFunction, + String extraName, + List warnExceptions + ) { this.declarationType = (TypeElement) processFunction.getEnclosingElement(); - this.processFunction = new ProcessFunction(processFunction, warnExceptions); + this.processFunction = new ProcessFunction(elements, types, processFunction, warnExceptions); this.implementation = ClassName.get( elements.getPackageOf(declarationType).toString(), @@ -87,6 +93,7 @@ private TypeSpec type() { } builder.addMethod(realEval(false)); builder.addMethod(toStringMethod()); + builder.addMethod(close()); return builder.build(); } @@ -219,6 +226,20 @@ private MethodSpec toStringMethod() { return builder.build(); } + private MethodSpec close() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("close").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + + List invocations = processFunction.args.stream().map(ProcessFunctionArg::closeInvocation).filter(s -> s != null).toList(); + if (invocations.isEmpty() == false) { + builder.addStatement( + "$T.closeExpectNoException(" + invocations.stream().collect(Collectors.joining(", ")) + ")", + Types.RELEASABLES + ); + } + return builder.build(); + } + private interface ProcessFunctionArg { /** * Type containing the actual data for a page of values for this field. Usually a @@ -276,7 +297,15 @@ private interface ProcessFunctionArg { */ void buildInvocation(StringBuilder pattern, List args, boolean blockStyle); + /** + * Accumulate invocation pattern and arguments to implement {@link Object#toString()}. + */ void buildToStringInvocation(StringBuilder pattern, List args, String prefix); + + /** + * The string to close this argument or {@code null}. + */ + String closeInvocation(); } private record StandardProcessFunctionArg(TypeName type, String name) implements ProcessFunctionArg { @@ -368,6 +397,11 @@ public void buildToStringInvocation(StringBuilder pattern, List args, St args.add(prefix + name + "="); args.add(name); } + + @Override + public String closeInvocation() { + return name; + } } private record ArrayProcessFunctionArg(TypeName componentType, String name) implements ProcessFunctionArg { @@ -470,9 +504,16 @@ public void buildToStringInvocation(StringBuilder pattern, List args, St args.add(Arrays.class); args.add(name); } + + @Override + public String closeInvocation() { + return "() -> Releasables.close(" + name + ")"; + } } - private record FixedProcessFunctionArg(TypeName type, String name, boolean includeInToString) implements ProcessFunctionArg { + private record FixedProcessFunctionArg(TypeName type, String name, boolean includeInToString, boolean releasable) + implements + ProcessFunctionArg { @Override public TypeName dataType(boolean blockStyle) { return type; @@ -534,6 +575,11 @@ public void buildToStringInvocation(StringBuilder pattern, List args, St args.add(name); } } + + @Override + public String closeInvocation() { + return releasable ? name : null; + } } private record BuilderProcessFunctionArg(ClassName type, String name) implements ProcessFunctionArg { @@ -593,6 +639,11 @@ public void buildInvocation(StringBuilder pattern, List args, boolean bl public void buildToStringInvocation(StringBuilder pattern, List args, String prefix) { // Don't want to include } + + @Override + public String closeInvocation() { + return null; + } } private static class ProcessFunction { @@ -601,7 +652,12 @@ private static class ProcessFunction { private final BuilderProcessFunctionArg builderArg; private final List warnExceptions; - private ProcessFunction(ExecutableElement function, List warnExceptions) { + private ProcessFunction( + Elements elements, + javax.lang.model.util.Types types, + ExecutableElement function, + List warnExceptions + ) { this.function = function; args = new ArrayList<>(); BuilderProcessFunctionArg builderArg = null; @@ -610,7 +666,14 @@ private ProcessFunction(ExecutableElement function, List warnExcepti String name = v.getSimpleName().toString(); Fixed fixed = v.getAnnotation(Fixed.class); if (fixed != null) { - args.add(new FixedProcessFunctionArg(type, name, fixed.includeInToString())); + args.add( + new FixedProcessFunctionArg( + type, + name, + fixed.includeInToString(), + Types.extendsSuper(types, v.asType(), "org.elasticsearch.core.Releasable") + ) + ); continue; } if (type instanceof ClassName c diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorProcessor.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorProcessor.java index d640e2b8633f2..e32cee86dc6e1 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorProcessor.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorProcessor.java @@ -75,6 +75,7 @@ public boolean process(Set set, RoundEnvironment roundEnv "evaluator", new EvaluatorImplementer( env.getElementUtils(), + env.getTypeUtils(), (ExecutableElement) evaluatorMethod, evaluatorAnn.extraName(), warnExceptions(evaluatorMethod) diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java index 4f98c10598bb8..dc59ecf757d20 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java @@ -12,8 +12,12 @@ import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; +import javax.lang.model.type.TypeMirror; + /** * Types used by the code generator. */ @@ -113,6 +117,8 @@ public class Types { static final ClassName BYTES_REF = ClassName.get("org.apache.lucene.util", "BytesRef"); + static final ClassName RELEASABLES = ClassName.get("org.elasticsearch.core", "Releasables"); + static ClassName blockType(TypeName elementType) { if (elementType.equals(TypeName.BOOLEAN)) { return BOOLEAN_BLOCK; @@ -265,4 +271,16 @@ static TypeName elementType(TypeName t) { throw new IllegalArgumentException("unknown element type for [" + t + "]"); } + static boolean extendsSuper(javax.lang.model.util.Types types, TypeMirror c, String superName) { + Deque mirrors = new ArrayDeque<>(); + mirrors.add(c); + while (mirrors.isEmpty() == false) { + TypeMirror m = mirrors.pop(); + if (m.toString().equals(superName)) { + return true; + } + mirrors.addAll(types.directSupertypes(m)); + } + return false; + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AbstractPageMappingOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AbstractPageMappingOperator.java index c32a45cb1407b..76f9250f24fb4 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AbstractPageMappingOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AbstractPageMappingOperator.java @@ -79,7 +79,7 @@ protected Status status(int pagesProcessed) { } @Override - public final void close() {} + public void close() {} public static class Status implements Operator.Status { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ColumnExtractOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ColumnExtractOperator.java index 3facf9edc7765..f1619323686db 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ColumnExtractOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ColumnExtractOperator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import java.util.function.Supplier; @@ -91,4 +92,8 @@ public interface Evaluator { void computeRow(BytesRefBlock input, int row, Block.Builder[] target, BytesRef spare); } + @Override + public void close() { + Releasables.closeExpectNoException(inputEvaluator); + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java index 8743c64db472a..e2a2f0b08452a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java @@ -7,6 +7,7 @@ package org.elasticsearch.compute.operator; +import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.core.Releasable; @@ -56,6 +57,13 @@ public BigArrays bigArrays() { return bigArrays; } + /** + * The {@link CircuitBreaker} to use to track memory. + */ + public CircuitBreaker breaker() { + return bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + } + public BlockFactory blockFactory() { return blockFactory; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java index 221c65f8a4ce3..3f307fb3fe115 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java @@ -9,6 +9,8 @@ import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; /** * Evaluates a tree of functions for every position in the block, resulting in a @@ -25,6 +27,7 @@ public Operator get(DriverContext driverContext) { @Override public String describe() { + // TODO ThrowingDriverContext blows up when combined with Concat return "EvalOperator[evaluator=" + evaluator.get(new ThrowingDriverContext()) + "]"; } } @@ -45,13 +48,23 @@ public String toString() { return getClass().getSimpleName() + "[evaluator=" + evaluator + "]"; } - public interface ExpressionEvaluator { + @Override + public void close() { + Releasables.closeExpectNoException(evaluator); + } + /** + * Evaluates an expression {@code a + b} or {@code log(c)} one {@link Page} at a time. + */ + public interface ExpressionEvaluator extends Releasable { /** A Factory for creating ExpressionEvaluators. */ interface Factory { ExpressionEvaluator get(DriverContext driverContext); } + /** + * Evaluate the expression. + */ Block eval(Page page); } @@ -65,5 +78,8 @@ public Block eval(Page page) { public String toString() { return "ConstantNull"; } + + @Override + public void close() {} }; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/FilterOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/FilterOperator.java index d3e7d6aa3a658..ec12bcac0e60f 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/FilterOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/FilterOperator.java @@ -11,6 +11,7 @@ import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import java.util.Arrays; @@ -79,4 +80,9 @@ protected Page process(Page page) { public String toString() { return "FilterOperator[" + "evaluator=" + evaluator + ']'; } + + @Override + public void close() { + Releasables.closeExpectNoException(evaluator); + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java index 03cd1442e3b9e..a6fc7484bbb9e 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java @@ -152,6 +152,9 @@ private MvDedupeEvaluator(EvalOperator.ExpressionEvaluator field) { public String toString() { return "MvDedupe[field=" + field + "]"; } + + @Override + public void close() {} } private MultivalueDedupe() {} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/StringExtractOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/StringExtractOperator.java index 92ec89a12aa78..59772cfa9f33d 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/StringExtractOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/StringExtractOperator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import java.util.Arrays; import java.util.Map; @@ -137,7 +138,8 @@ public String toString() { return "StringExtractOperator[fields=[" + Arrays.stream(fieldNames).collect(Collectors.joining(", ")) + "]]"; } - public interface ExtractEvaluator { - Map computeRow(Page page, int position); + @Override + public void close() { + Releasables.closeExpectNoException(inputEvaluator); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java index a324aba48b539..86b3a18992db4 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java @@ -205,14 +205,7 @@ public record TopNOperatorFactory( @Override public TopNOperator get(DriverContext driverContext) { - return new TopNOperator( - driverContext.bigArrays().breakerService().getBreaker(CircuitBreaker.REQUEST), - topCount, - elementTypes, - encoders, - sortOrders, - maxPageSize - ); + return new TopNOperator(driverContext.breaker(), topCount, elementTypes, encoders, sortOrders, maxPageSize); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java index c9b8dae1f9671..7825e035df0db 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java @@ -47,7 +47,19 @@ public String toString() { @Override protected Operator.OperatorFactory simple(BigArrays bigArrays) { Supplier expEval = () -> new FirstWord(0); - return new ColumnExtractOperator.Factory(new ElementType[] { ElementType.BYTES_REF }, dvrCtx -> page -> page.getBlock(0), expEval); + return new ColumnExtractOperator.Factory( + new ElementType[] { ElementType.BYTES_REF }, + dvrCtx -> new EvalOperator.ExpressionEvaluator() { + @Override + public Block eval(Page page) { + return page.getBlock(0); + } + + @Override + public void close() {} + }, + expEval + ); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java index 486a4be23f4c7..156f37d8d8e7a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java @@ -36,6 +36,9 @@ public Block eval(Page page) { } return result.build().asBlock(); } + + @Override + public void close() {} } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java index 146bafcd628e8..b26fe0c33fe1c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java @@ -38,6 +38,9 @@ public Block eval(Page page) { } return result.build().asBlock(); } + + @Override + public void close() {} } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java index 70cef5adec308..f3c67f18589fa 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java @@ -42,7 +42,19 @@ public Map apply(String s) { @Override protected Operator.OperatorFactory simple(BigArrays bigArrays) { Supplier>> expEval = () -> new FirstWord("test"); - return new StringExtractOperator.StringExtractOperatorFactory(new String[] { "test" }, dvrCtx -> page -> page.getBlock(0), expEval); + return new StringExtractOperator.StringExtractOperatorFactory( + new String[] { "test" }, + dvrCtx -> new EvalOperator.ExpressionEvaluator() { + @Override + public Block eval(Page page) { + return page.getBlock(0); + } + + @Override + public void close() {} + }, + expEval + ); } @Override @@ -77,11 +89,15 @@ protected ByteSizeValue smallEnoughToCircuitBreak() { public void testMultivalueDissectInput() { - StringExtractOperator operator = new StringExtractOperator( - new String[] { "test" }, - (page) -> page.getBlock(0), - new FirstWord("test") - ); + StringExtractOperator operator = new StringExtractOperator(new String[] { "test" }, new EvalOperator.ExpressionEvaluator() { + @Override + public Block eval(Page page) { + return page.getBlock(0); + } + + @Override + public void close() {} + }, new FirstWord("test")); BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(1); builder.beginPositionEntry(); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java index 7e74b54b74086..da2d698190fd7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Equals}. @@ -82,4 +83,9 @@ public BooleanVector eval(int positionCount, BooleanVector lhsVector, BooleanVec public String toString() { return "EqualsBoolsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java index 0f52038f67ec7..30e12b2a64b15 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Equals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "EqualsDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java index 10491dcdf73dd..945bc1c6f96a7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Equals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "EqualsIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java index aa2d09be9bf72..826c78814e31c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Equals}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "EqualsKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java index ad262b88c1641..7065a01895771 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Equals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "EqualsLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java index ed6da288d1e75..cc91e93a661fb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "GreaterThanDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java index 2ae3c956c5199..6ace6ae8d24dc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "GreaterThanIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java index 53b0fe8dc8ef9..6d0e4f39f0e55 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThan}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "GreaterThanKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java index 595f8f9fb1172..68a406f237bf7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "GreaterThanLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java index dd5eb8d86467a..8077ad440b41c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "GreaterThanOrEqualDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java index 05d173d4e99b1..ee3be0d1def76 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "GreaterThanOrEqualIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java index d4bc72d576c7b..6f1b37411ccf9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThanOrEqual}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "GreaterThanOrEqualKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java index 5f7a4ad79217f..d64ffdb8392f7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link GreaterThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "GreaterThanOrEqualLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java index 065486d6a38fa..1e1cabaccfbfe 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "LessThanDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java index a47a4cc72fafb..2505da4e6aa63 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "LessThanIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java index d1d024075feef..9a9dd94411590 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThan}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "LessThanKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java index 2aa35ea470649..aa5dcc8ef6ff8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThan}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "LessThanLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java index 21a4bb97f42ea..9460ffe577896 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "LessThanOrEqualDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java index db6130e498e2a..1c72bb13f7831 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "LessThanOrEqualIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java index 46849d4b450f1..20fc5d6420da4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThanOrEqual}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "LessThanOrEqualKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java index 4e05484d18f30..35b54c325ae3f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LessThanOrEqual}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "LessThanOrEqualLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java index a68b972ed0c52..d795a3ea31476 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link NotEquals}. @@ -82,4 +83,9 @@ public BooleanVector eval(int positionCount, BooleanVector lhsVector, BooleanVec public String toString() { return "NotEqualsBoolsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java index fee6915f54e05..e3bd7dcb87de1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link NotEquals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, DoubleVector lhsVector, DoubleVecto public String toString() { return "NotEqualsDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java index e488cd0d16c23..94e297c6f5e7e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link NotEquals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, IntVector lhsVector, IntVector rhsV public String toString() { return "NotEqualsIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java index 0f6a113062826..820ec32dc944c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link NotEquals}. @@ -89,4 +90,9 @@ public BooleanVector eval(int positionCount, BytesRefVector lhsVector, BytesRefV public String toString() { return "NotEqualsKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java index 21384ead1e745..563f1e4c24b57 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link NotEquals}. @@ -84,4 +85,9 @@ public BooleanVector eval(int positionCount, LongVector lhsVector, LongVector rh public String toString() { return "NotEqualsLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java index 98384f9cf0203..dc061192a38c7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Not}. @@ -65,4 +66,9 @@ public BooleanVector eval(int positionCount, BooleanVector vVector) { public String toString() { return "NotEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java index 001b0e1702f61..87916f3fb38a5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RegexMatch}. @@ -75,4 +76,9 @@ public BooleanVector eval(int positionCount, BytesRefVector inputVector) { public String toString() { return "RegexMatchEvaluator[" + "input=" + input + ", pattern=" + pattern + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(input); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java index 463c10a14ee5e..a3c3328ec0f95 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}. @@ -86,4 +87,9 @@ public BooleanVector eval(int positionCount, BooleanVector[] valuesVectors) { public String toString() { return "GreatestBooleanEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java index f6d6b62b5d3bd..0148253c97711 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}. @@ -95,4 +96,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector[] valuesVectors) { public String toString() { return "GreatestBytesRefEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java index 22ae82dc0d0ac..49e8aa3e5cdae 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}. @@ -86,4 +87,9 @@ public DoubleVector eval(int positionCount, DoubleVector[] valuesVectors) { public String toString() { return "GreatestDoubleEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java index c2cc45bc8b180..abd710005977e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}. @@ -86,4 +87,9 @@ public IntVector eval(int positionCount, IntVector[] valuesVectors) { public String toString() { return "GreatestIntEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java index e148c55b36d61..af1fb0b99b60e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}. @@ -86,4 +87,9 @@ public LongVector eval(int positionCount, LongVector[] valuesVectors) { public String toString() { return "GreatestLongEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java index 8aa566235c035..98c9a8d7b2fe9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Least}. @@ -86,4 +87,9 @@ public BooleanVector eval(int positionCount, BooleanVector[] valuesVectors) { public String toString() { return "LeastBooleanEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java index c4dd29e583169..a05eb2cf41c69 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Least}. @@ -95,4 +96,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector[] valuesVectors) { public String toString() { return "LeastBytesRefEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java index 43ada40582c31..628d3071d0d69 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Least}. @@ -86,4 +87,9 @@ public DoubleVector eval(int positionCount, DoubleVector[] valuesVectors) { public String toString() { return "LeastDoubleEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java index 15396c59dfa2c..d83403e5d5dc3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Least}. @@ -85,4 +86,9 @@ public IntVector eval(int positionCount, IntVector[] valuesVectors) { public String toString() { return "LeastIntEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java index 2115d9c19b7fb..18d2d8994a106 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Least}. @@ -86,4 +87,9 @@ public LongVector eval(int positionCount, LongVector[] valuesVectors) { public String toString() { return "LeastLongEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java index bae85f15dc525..a0f4e6215f0f8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link DateExtract}. @@ -74,4 +75,9 @@ public LongVector eval(int positionCount, LongVector valueVector) { public String toString() { return "DateExtractConstantEvaluator[" + "value=" + value + ", chronoField=" + chronoField + ", zone=" + zone + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(value); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java index 0704a84e9d39e..1a052c8c5b03b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java @@ -17,6 +17,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -108,4 +109,9 @@ public LongBlock eval(int positionCount, LongVector valueVector, public String toString() { return "DateExtractEvaluator[" + "value=" + value + ", chronoField=" + chronoField + ", zone=" + zone + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(value, chronoField); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java index fdfc28b0af3b9..d724c1d27d81e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link DateFormat}. @@ -72,4 +73,9 @@ public BytesRefVector eval(int positionCount, LongVector valVector) { public String toString() { return "DateFormatConstantEvaluator[" + "val=" + val + ", formatter=" + formatter + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java index 7fbb705c95335..1e1b9a9cbd366 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link DateFormat}. @@ -92,4 +93,9 @@ public BytesRefVector eval(int positionCount, LongVector valVector, public String toString() { return "DateFormatEvaluator[" + "val=" + val + ", formatter=" + formatter + ", locale=" + locale + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, formatter); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java index 8a714985c666d..0a1029f479f6d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -90,4 +91,9 @@ public LongBlock eval(int positionCount, BytesRefVector valVector) { public String toString() { return "DateParseConstantEvaluator[" + "val=" + val + ", formatter=" + formatter + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java index cc0cfc62a1921..00eedcdf78c43 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -109,4 +110,9 @@ public LongBlock eval(int positionCount, BytesRefVector valVector, public String toString() { return "DateParseEvaluator[" + "val=" + val + ", formatter=" + formatter + ", zoneId=" + zoneId + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, formatter); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java index 79c36712313a0..1f6592b368dbc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link DateTrunc}. @@ -70,4 +71,9 @@ public LongVector eval(int positionCount, LongVector fieldValVector) { public String toString() { return "DateTruncEvaluator[" + "fieldVal=" + fieldVal + ", rounding=" + rounding + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(fieldVal); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java index 49cf0727b4781..b07f07861076c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java @@ -43,4 +43,8 @@ public LongVector eval(int positionCount) { public String toString() { return "NowEvaluator[" + "now=" + now + "]"; } + + @Override + public void close() { + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java index 6241093a607c8..77810a8441471 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link CIDRMatch}. @@ -116,4 +117,9 @@ public BooleanVector eval(int positionCount, BytesRefVector ipVector, public String toString() { return "CIDRMatchEvaluator[" + "ip=" + ip + ", cidrs=" + Arrays.toString(cidrs) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(ip, () -> Releasables.close(cidrs)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java index f9e8f72d981c2..c6f1d44b42226 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Abs}. @@ -66,4 +67,9 @@ public DoubleVector eval(int positionCount, DoubleVector fieldValVector) { public String toString() { return "AbsDoubleEvaluator[" + "fieldVal=" + fieldVal + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(fieldVal); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java index e974bd69063e5..2ded2d1937765 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Abs}. @@ -65,4 +66,9 @@ public IntVector eval(int positionCount, IntVector fieldValVector) { public String toString() { return "AbsIntEvaluator[" + "fieldVal=" + fieldVal + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(fieldVal); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java index f2e01666c25e6..f2085df977695 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Abs}. @@ -65,4 +66,9 @@ public LongVector eval(int positionCount, LongVector fieldValVector) { public String toString() { return "AbsLongEvaluator[" + "fieldVal=" + fieldVal + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(fieldVal); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java index c966c0599bb51..84d6969f67e51 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "AcosEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java index 159734187f8e5..303a1a21a9c17 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "AsinEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java index 3a3bcf2594495..ffc903c70e3d5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Atan2}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector yVector, DoubleVector x public String toString() { return "Atan2Evaluator[" + "y=" + y + ", x=" + x + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(y, x); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java index 6896e65441c86..fe2152f9c1fda 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Atan}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "AtanEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java index 61709f909b850..84e8de91862f1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -67,4 +68,9 @@ public DoubleVector eval(int positionCount, IntVector vVector) { public String toString() { return "CastIntToDoubleEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java index 83628ea5962d7..e260f157b4a47 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -67,4 +68,9 @@ public LongVector eval(int positionCount, IntVector vVector) { public String toString() { return "CastIntToLongEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java index 4e52416b98654..d908234423bac 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -68,4 +69,9 @@ public LongVector eval(int positionCount, IntVector vVector) { public String toString() { return "CastIntToUnsignedLongEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java index f3a5501edf781..8406af3f21b8e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -68,4 +69,9 @@ public DoubleVector eval(int positionCount, LongVector vVector) { public String toString() { return "CastLongToDoubleEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java index 23179757e7532..8d91999065e30 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -66,4 +67,9 @@ public LongVector eval(int positionCount, LongVector vVector) { public String toString() { return "CastLongToUnsignedLongEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java index 0f149bf7ae340..6dcad54ea4c61 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cast}. @@ -68,4 +69,9 @@ public DoubleVector eval(int positionCount, LongVector vVector) { public String toString() { return "CastUnsignedLongToDoubleEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java index 79f8143564a42..d27cc25407a88 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Ceil}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "CeilDoubleEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java index bcf054989d31c..ddc796cee6c43 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Cos}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "CosEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java index 73819619937ed..01f7b1a6ba3d1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "CoshEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java index 75db4ac7b2b38..c40444a4f5617 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Floor}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "FloorDoubleEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java index f17b7dfe91c21..1de4689d1f652 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link IsFinite}. @@ -67,4 +68,9 @@ public BooleanVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "IsFiniteEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java index 37b1f58efe49f..637b18ea212a7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link IsInfinite}. @@ -67,4 +68,9 @@ public BooleanVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "IsInfiniteEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java index 1fff9b81f08e9..6174d38f602a4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link IsNaN}. @@ -67,4 +68,9 @@ public BooleanVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "IsNaNEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java index 03175592f1df3..569e0560406ec 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "Log10DoubleEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java index 11ce92c7f17ff..0d70b89e0e673 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -83,4 +84,9 @@ public DoubleBlock eval(int positionCount, IntVector valVector) { public String toString() { return "Log10IntEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java index 658f0575e9e94..52123291d8c01 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -83,4 +84,9 @@ public DoubleBlock eval(int positionCount, LongVector valVector) { public String toString() { return "Log10LongEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java index e179ac4cb8f27..4de00fe591a5f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -83,4 +84,9 @@ public DoubleBlock eval(int positionCount, LongVector valVector) { public String toString() { return "Log10UnsignedLongEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java index 6ff5f5f0d510e..d129596790441 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public DoubleBlock eval(int positionCount, DoubleVector baseVector, DoubleVector public String toString() { return "PowDoubleEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(base, exponent); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java index 70a663d2b933e..ae8f528c4075f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -99,4 +100,9 @@ public IntBlock eval(int positionCount, DoubleVector baseVector, DoubleVector ex public String toString() { return "PowIntEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(base, exponent); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java index 870f0b75dedca..e9509f24a74b4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -99,4 +100,9 @@ public LongBlock eval(int positionCount, DoubleVector baseVector, DoubleVector e public String toString() { return "PowLongEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(base, exponent); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java index 4d4a3deb2cc7a..55119794db66f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Round}. @@ -84,4 +85,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector, LongVector d public String toString() { return "RoundDoubleEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, decimals); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java index eeb5eedf25f0d..9c928d5a785d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Round}. @@ -66,4 +67,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "RoundDoubleNoDecimalsEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java index e73bbc05f72c2..6ae57c874a8d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Round}. @@ -84,4 +85,9 @@ public IntVector eval(int positionCount, IntVector valVector, LongVector decimal public String toString() { return "RoundIntEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, decimals); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java index e2bfc94b4205f..221473cbe5931 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Round}. @@ -82,4 +83,9 @@ public LongVector eval(int positionCount, LongVector valVector, LongVector decim public String toString() { return "RoundLongEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, decimals); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java index 15bbc619a66d6..5a1051a48515b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Round}. @@ -82,4 +83,9 @@ public LongVector eval(int positionCount, LongVector valVector, LongVector decim public String toString() { return "RoundUnsignedLongEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val, decimals); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java index a7483e84ee730..ef03377a9e3de 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Sin}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "SinEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java index ba985f76fac20..7450438dc064a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "SinhEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java index 39d15c6e143cc..6743e9548e189 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public DoubleBlock eval(int positionCount, DoubleVector valVector) { public String toString() { return "SqrtDoubleEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java index 6fc49da574015..0ad276c45397d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -83,4 +84,9 @@ public DoubleBlock eval(int positionCount, IntVector valVector) { public String toString() { return "SqrtIntEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java index 8506b02f1aa27..dc2c4c9598cda 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -83,4 +84,9 @@ public DoubleBlock eval(int positionCount, LongVector valVector) { public String toString() { return "SqrtLongEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java index 1b01e45679ad4..736cf62284a7c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Sqrt}. @@ -68,4 +69,9 @@ public DoubleVector eval(int positionCount, LongVector valVector) { public String toString() { return "SqrtUnsignedLongEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java index 387a4b6148e1e..2477d395fafb8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Tan}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "TanEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java index 451a85aa71c22..63b30fec6009c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Tanh}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector valVector) { public String toString() { return "TanhEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java index 868e6338536e8..d1e45b8ca68c7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java @@ -8,26 +8,27 @@ import java.lang.String; import java.util.Arrays; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.BytesRefBuilder; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Concat}. * This class is generated. Do not edit it. */ public final class ConcatEvaluator implements EvalOperator.ExpressionEvaluator { - private final BytesRefBuilder scratch; + private final BreakingBytesRefBuilder scratch; private final EvalOperator.ExpressionEvaluator[] values; private final DriverContext driverContext; - public ConcatEvaluator(BytesRefBuilder scratch, EvalOperator.ExpressionEvaluator[] values, + public ConcatEvaluator(BreakingBytesRefBuilder scratch, EvalOperator.ExpressionEvaluator[] values, DriverContext driverContext) { this.scratch = scratch; this.values = values; @@ -99,4 +100,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector[] valuesVectors) { public String toString() { return "ConcatEvaluator[" + "values=" + Arrays.toString(values) + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(scratch, () -> Releasables.close(values)); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java index 4206dc8241058..a03b169d0bb2f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link EndsWith}. @@ -90,4 +91,9 @@ public BooleanVector eval(int positionCount, BytesRefVector strVector, public String toString() { return "EndsWithEvaluator[" + "str=" + str + ", suffix=" + suffix + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, suffix); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java index 594f521c76da5..4e5e567192082 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link LTrim}. @@ -68,4 +69,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector valVector) { public String toString() { return "LTrimEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java index 05da38fe7d09d..ecdf34a86c4a3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Left}. @@ -95,4 +96,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector strVector, IntVecto public String toString() { return "LeftEvaluator[" + "str=" + str + ", length=" + length + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, length); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java index 38d9adaf1184c..d18a6bdaa5606 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Length}. @@ -70,4 +71,9 @@ public IntVector eval(int positionCount, BytesRefVector valVector) { public String toString() { return "LengthEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java index f1a07fbcbd7f4..e128b73c55ee4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RTrim}. @@ -68,4 +69,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector valVector) { public String toString() { return "RTrimEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java index 35d05b53faf04..691c6dece4d2f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Right}. @@ -95,4 +96,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector strVector, IntVecto public String toString() { return "RightEvaluator[" + "str=" + str + ", length=" + length + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, length); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java index 29ee86b1aeca2..09bb5b626eec8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Split}. @@ -75,4 +76,9 @@ public BytesRefBlock eval(int positionCount, BytesRefVector strVector) { public String toString() { return "SplitSingleByteEvaluator[" + "str=" + str + ", delim=" + delim + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java index 8577aa7cc3fc7..09be20be5bb48 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Split}. @@ -91,4 +92,9 @@ public BytesRefBlock eval(int positionCount, BytesRefVector strVector, public String toString() { return "SplitVariableEvaluator[" + "str=" + str + ", delim=" + delim + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, delim); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java index 1ec6f97836025..94a21bc188047 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StartsWith}. @@ -90,4 +91,9 @@ public BooleanVector eval(int positionCount, BytesRefVector strVector, public String toString() { return "StartsWithEvaluator[" + "str=" + str + ", prefix=" + prefix + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, prefix); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java index 3c86a51c513dc..a3c0aac4f4ba5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Substring}. @@ -106,4 +107,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector strVector, IntVecto public String toString() { return "SubstringEvaluator[" + "str=" + str + ", start=" + start + ", length=" + length + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, start, length); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java index cbe96b7056a75..199d1779fc250 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Substring}. @@ -87,4 +88,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector strVector, IntVecto public String toString() { return "SubstringNoLengthEvaluator[" + "str=" + str + ", start=" + start + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(str, start); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java index dceffc22cec7c..765d622cb2597 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Trim}. @@ -68,4 +69,9 @@ public BytesRefVector eval(int positionCount, BytesRefVector valVector) { public String toString() { return "TrimEvaluator[" + "val=" + val + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(val); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java index 5562085ba01fd..801116d5f181e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -87,4 +88,9 @@ public LongBlock eval(int positionCount, LongVector datetimeVector) { public String toString() { return "AddDatetimesEvaluator[" + "datetime=" + datetime + ", temporalAmount=" + temporalAmount + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(datetime); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java index 86a4f83045725..be11848f7599a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Add}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector lhsVector, DoubleVector public String toString() { return "AddDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java index 1c9e2227af750..4c2e1a53221b9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public IntBlock eval(int positionCount, IntVector lhsVector, IntVector rhsVector public String toString() { return "AddIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java index 80d401d42bf02..eeb2ecc5b5c26 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "AddLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java index e48572f7980d1..2684949674da5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "AddUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java index e4ce48a844de3..356b6eba09b7a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Div}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector lhsVector, DoubleVector public String toString() { return "DivDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java index 8b905ca7a0a4f..3bfb8b6ca26c9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public IntBlock eval(int positionCount, IntVector lhsVector, IntVector rhsVector public String toString() { return "DivIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java index 155beeff58f45..506fda9d33345 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "DivLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java index f5c949351bb0f..6500d7222671a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "DivUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java index 7abb9cdca124d..0634573da6439 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Mod}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector lhsVector, DoubleVector public String toString() { return "ModDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java index 4ba618a162e60..55fa8ceea09bf 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public IntBlock eval(int positionCount, IntVector lhsVector, IntVector rhsVector public String toString() { return "ModIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java index 8fb43d2968947..a1ad1d0c92c75 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "ModLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java index 664d3219e0319..c7462ae237eff 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "ModUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java index d5079ce565c14..cded768003656 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Mul}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector lhsVector, DoubleVector public String toString() { return "MulDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java index e5b9917c409e8..24be9244a5710 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public IntBlock eval(int positionCount, IntVector lhsVector, IntVector rhsVector public String toString() { return "MulIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java index 002793ec781d9..b8c1e1048e1e6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "MulLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java index fe0d1ae2f6e7b..e427a89cab197 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "MulUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java index e5d1284ade262..6bc8eb17d76c0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Neg}. @@ -65,4 +66,9 @@ public DoubleVector eval(int positionCount, DoubleVector vVector) { public String toString() { return "NegDoublesEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java index 526ce41bd71d6..b485d1952ccaa 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public IntBlock eval(int positionCount, IntVector vVector) { public String toString() { return "NegIntsEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java index 21b02d8f0783a..704255f81b46a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -82,4 +83,9 @@ public LongBlock eval(int positionCount, LongVector vVector) { public String toString() { return "NegLongsEvaluator[" + "v=" + v + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(v); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java index 681edc68f326e..6841b2193f99e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java @@ -15,6 +15,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -87,4 +88,9 @@ public LongBlock eval(int positionCount, LongVector datetimeVector) { public String toString() { return "SubDatetimesEvaluator[" + "datetime=" + datetime + ", temporalAmount=" + temporalAmount + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(datetime); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java index bf69f6a7a9a81..8adfb0f12d300 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Sub}. @@ -82,4 +83,9 @@ public DoubleVector eval(int positionCount, DoubleVector lhsVector, DoubleVector public String toString() { return "SubDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java index 8d5afb8113a99..49b8a42bb6094 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public IntBlock eval(int positionCount, IntVector lhsVector, IntVector rhsVector public String toString() { return "SubIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java index 5f561c9778b18..f4a7d218252a5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "SubLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java index a30d9d973c2d8..73ac871746356 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.ql.tree.Source; @@ -98,4 +99,9 @@ public LongBlock eval(int positionCount, LongVector lhsVector, LongVector rhsVec public String toString() { return "SubUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(lhs, rhs); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java index 3688a0633aca9..dc5f1ced16c11 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java @@ -18,6 +18,7 @@ import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.evaluator.mapper.ExpressionMapper; import org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.ComparisonMapper; @@ -138,6 +139,10 @@ private Block eval(BooleanVector lhs, BooleanVector rhs) { return result.build().asBlock(); } + @Override + public void close() { + Releasables.closeExpectNoException(leftEval, rightEval); + } } return driverContext -> new BooleanLogicExpressionEvaluator(bc, leftEval.get(driverContext), rightEval.get(driverContext)); } @@ -162,6 +167,9 @@ record Attribute(int channel) implements ExpressionEvaluator { public Block eval(Page page) { return page.getBlock(channel); } + + @Override + public void close() {} } int channel = layout.get(attr.id()).channel(); return driverContext -> new Attribute(channel); @@ -177,6 +185,9 @@ record LiteralsEvaluator(IntFunction block) implements ExpressionEvaluato public Block eval(Page page) { return block.apply(page.getPositionCount()); } + + @Override + public void close() {} } // wrap the closure to provide a nice toString (used by tests) var blockClosure = new IntFunction() { @@ -234,6 +245,11 @@ public Block eval(Page page) { } return new BooleanArrayVector(result, result.length).asBlock(); } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } } } @@ -258,6 +274,11 @@ public Block eval(Page page) { } return new BooleanArrayVector(result, result.length).asBlock(); } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java index a518dd36e3e9e..e26b7891be570 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java @@ -7,6 +7,9 @@ package org.elasticsearch.xpack.esql.evaluator.mapper; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.NoopCircuitBreaker; +import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.compute.operator.ThrowingDriverContext; @@ -30,9 +33,20 @@ public interface EvaluatorMapper { * good enough. */ default Object fold() { - return toJavaObject( - toEvaluator(e -> driverContext -> p -> fromArrayRow(e.fold())[0]).get(new ThrowingDriverContext()).eval(new Page(1)), - 0 - ); + return toJavaObject(toEvaluator(e -> driverContext -> new ExpressionEvaluator() { + @Override + public Block eval(Page page) { + return fromArrayRow(e.fold())[0]; + } + + @Override + public void close() {} + }).get(new ThrowingDriverContext() { + @Override + public CircuitBreaker breaker() { + // TODO maybe this should have a small fixed limit? + return new NoopCircuitBreaker(CircuitBreaker.REQUEST); + } + }).eval(new Page(1)), 0); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/InMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/InMapper.java index 89df014a73705..8f6ec95123549 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/InMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/InMapper.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.evaluator.mapper.ExpressionMapper; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In; import org.elasticsearch.xpack.esql.planner.Layout; @@ -114,5 +115,10 @@ private static Block evalWithNulls(boolean[] values, BitSet nulls, boolean nullI } } } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(listEvaluators)); + } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java index fd8c400b834f0..909b27e20530d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java @@ -14,6 +14,8 @@ import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.ql.expression.Expression; @@ -174,7 +176,12 @@ public ConditionEvaluator apply(DriverContext driverContext) { } } - record ConditionEvaluator(EvalOperator.ExpressionEvaluator condition, EvalOperator.ExpressionEvaluator value) {} + record ConditionEvaluator(EvalOperator.ExpressionEvaluator condition, EvalOperator.ExpressionEvaluator value) implements Releasable { + @Override + public void close() { + Releasables.closeExpectNoException(condition, value); + } + } private record CaseEvaluator(ElementType resultType, List conditions, EvalOperator.ExpressionEvaluator elseVal) implements @@ -216,5 +223,10 @@ public Block eval(Page page) { } return result.build(); } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(conditions), elseVal); + } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java index 257ae59d5f8b1..1af66cb4f50b0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.Warnings; @@ -118,5 +119,11 @@ protected final void registerException(Exception exception) { public final String toString() { return name() + "Evaluator[field=" + fieldEvaluator + "]"; } + + @Override + public void close() { + // TODO toString allocates - we should probably check breakers there too + Releasables.closeExpectNoException(fieldEvaluator); + } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java index cbc40d0c2e73e..4179b7d0f750d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java @@ -8,7 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.ql.QlIllegalArgumentException; @@ -26,7 +26,7 @@ public static ExpressionEvaluator.Factory cast(DataType current, DataType requir return in; } if (current == DataTypes.NULL || required == DataTypes.NULL) { - return dvrCtx -> page -> Block.constantNullBlock(page.getPositionCount()); + return dvrCtx -> EvalOperator.CONSTANT_NULL; } if (required == DataTypes.DOUBLE) { if (current == DataTypes.LONG) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java index 5bd3609461530..3291ab01f56a9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.ql.expression.Expression; @@ -126,5 +127,10 @@ public Block eval(Page page) { public final String toString() { return name() + "[field=" + field + "]"; } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java index 8cd8e4b3b2509..43b305ab1b07a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java @@ -107,7 +107,7 @@ public final Block eval(Page page) { int positionCount = page.getPositionCount(); BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); - BytesRefBuilder work = new BytesRefBuilder(); + BytesRefBuilder work = new BytesRefBuilder(); // TODO BreakingBytesRefBuilder so we don't blow past circuit breakers BytesRef fieldScratch = new BytesRef(); BytesRef delimScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -142,5 +142,8 @@ public final Block eval(Page page) { public final String toString() { return "MvConcat[field=" + field + ", delim=" + delim + "]"; } + + @Override + public void close() {} } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java index b963575826182..410196f0785f3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java @@ -12,6 +12,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.ql.expression.Expression; @@ -162,5 +163,10 @@ public Block eval(Page page) { public String toString() { return "CoalesceEvaluator[values=" + evaluators + ']'; } + + @Override + public void close() { + Releasables.closeExpectNoException(() -> Releasables.close(evaluators)); + } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java index 4bde51fe28579..ae805975646a6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java @@ -8,9 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.string; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.BytesRefBuilder; import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.ann.Fixed; +import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; @@ -75,19 +75,19 @@ public Object fold() { public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var values = children().stream().map(toEvaluator).toList(); return dvrCtx -> new ConcatEvaluator( - new BytesRefBuilder(), + new BreakingBytesRefBuilder(dvrCtx.breaker(), "concat"), values.stream().map(fac -> fac.get(dvrCtx)).toArray(EvalOperator.ExpressionEvaluator[]::new), dvrCtx ); } @Evaluator - static BytesRef process(@Fixed(includeInToString = false) BytesRefBuilder scratch, BytesRef[] values) { + static BytesRef process(@Fixed(includeInToString = false) BreakingBytesRefBuilder scratch, BytesRef[] values) { scratch.clear(); for (int i = 0; i < values.length; i++) { scratch.append(values[i]); } - return scratch.get(); + return scratch.bytesRefView(); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java index 7d332ce28025d..3ea8ffe242919 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/ComputeService.java @@ -255,7 +255,9 @@ void runCompute(CancellableTask task, ComputeContext context, PhysicalPlan plan, plan = PlannerUtils.localPlan(context.searchContexts, context.configuration, plan); LocalExecutionPlanner.LocalExecutionPlan localExecutionPlan = planner.plan(plan); - LOGGER.debug("Local execution plan:\n{}", localExecutionPlan.describe()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Local execution plan:\n{}", localExecutionPlan.describe()); + } drivers = localExecutionPlan.createDrivers(context.sessionId); if (drivers.isEmpty()) { throw new IllegalStateException("no drivers created"); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index caf907c94feb5..ab78640030fb1 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -393,7 +393,7 @@ protected void start(Driver driver, ActionListener driverListener) { } }; PlainActionFuture future = new PlainActionFuture<>(); - runner.runToCompletion(drivers, future); + runner.runToCompletion(drivers, ActionListener.releaseAfter(future, () -> Releasables.close(drivers))); future.actionGet(TimeValue.timeValueSeconds(30)); var responseHeaders = threadPool.getThreadContext().getResponseHeaders(); return new ActualResults(columnNames, columnTypes, dataTypes, collectedPages, responseHeaders); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 25e6c3672f020..8f19ba7cbb584 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -10,6 +10,8 @@ import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.sandbox.document.HalfFloatPoint; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; @@ -20,7 +22,6 @@ import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.core.PathUtils; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.logging.LogManager; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.esql.evaluator.EvalMapper; @@ -184,7 +185,10 @@ public final void testEvaluate() { expression = new FoldNull().rule(expression); assertThat(expression.dataType(), equalTo(testCase.expectedType)); // TODO should we convert unsigned_long into BigDecimal so it's easier to assert? - Object result = toJavaObject(evaluator(expression).get(driverContext()).eval(row(testCase.getDataValues())), 0); + Object result; + try (ExpressionEvaluator evaluator = evaluator(expression).get(driverContext())) { + result = toJavaObject(evaluator.eval(row(testCase.getDataValues())), 0); + } assertThat(result, not(equalTo(Double.NaN))); assertThat(result, not(equalTo(Double.POSITIVE_INFINITY))); assertThat(result, not(equalTo(Double.NEGATIVE_INFINITY))); @@ -198,21 +202,22 @@ public final void testSimpleWithNulls() { // TODO replace this with nulls insert assumeTrue("nothing to do if a type error", testCase.getExpectedTypeError() == null); assumeTrue("All test data types must be representable in order to build fields", testCase.allTypesAreRepresentable()); List simpleData = testCase.getDataValues(); - EvalOperator.ExpressionEvaluator eval = evaluator(buildFieldExpression(testCase)).get(driverContext()); - Block[] orig = BlockUtils.fromListRow(simpleData); - for (int i = 0; i < orig.length; i++) { - List data = new ArrayList<>(); - Block[] blocks = new Block[orig.length]; - for (int b = 0; b < blocks.length; b++) { - if (b == i) { - blocks[b] = orig[b].elementType().newBlockBuilder(1).appendNull().build(); - data.add(null); - } else { - blocks[b] = orig[b]; - data.add(simpleData.get(b)); + try (EvalOperator.ExpressionEvaluator eval = evaluator(buildFieldExpression(testCase)).get(driverContext())) { + Block[] orig = BlockUtils.fromListRow(simpleData); + for (int i = 0; i < orig.length; i++) { + List data = new ArrayList<>(); + Block[] blocks = new Block[orig.length]; + for (int b = 0; b < blocks.length; b++) { + if (b == i) { + blocks[b] = orig[b].elementType().newBlockBuilder(1).appendNull().build(); + data.add(null); + } else { + blocks[b] = orig[b]; + data.add(simpleData.get(b)); + } } + assertSimpleWithNulls(data, eval.eval(new Page(blocks)), i); } - assertSimpleWithNulls(data, eval.eval(new Page(blocks)), i); } } @@ -235,9 +240,10 @@ public final void testEvaluateInManyThreads() throws ExecutionException, Interru Page page = row(simpleData); futures.add(exec.submit(() -> { - EvalOperator.ExpressionEvaluator eval = evalSupplier.get(driverContext()); - for (int c = 0; c < count; c++) { - assertThat(toJavaObject(eval.eval(page), 0), testCase.getMatcher()); + try (EvalOperator.ExpressionEvaluator eval = evalSupplier.get(driverContext())) { + for (int c = 0; c < count; c++) { + assertThat(toJavaObject(eval.eval(page), 0), testCase.getMatcher()); + } } })); } @@ -253,8 +259,9 @@ public final void testEvaluatorToString() { assumeTrue("nothing to do if a type error", testCase.getExpectedTypeError() == null); assumeTrue("All test data types must be representable in order to build fields", testCase.allTypesAreRepresentable()); var supplier = evaluator(buildFieldExpression(testCase)); - var ev = supplier.get(driverContext()); - assertThat(ev.toString(), equalTo(testCase.evaluatorToString)); + try (ExpressionEvaluator ev = supplier.get(driverContext())) { + assertThat(ev.toString(), equalTo(testCase.evaluatorToString)); + } } public final void testFold() { @@ -395,7 +402,7 @@ protected static List errorsForCasesWithoutExamples(List types.stream().filter(t -> t == DataTypes.NULL).count() <= 1) - .map(types -> typeErrorSupplier(validPerPosition, types)) + .map(types -> typeErrorSupplier(validPerPosition.size() != 1, validPerPosition, types)) .forEach(suppliers::add); return suppliers; } @@ -439,13 +446,17 @@ private static List append(List orig, DataType extra) { /** * Build a test case that asserts that the combination of parameter types is an error. */ - private static TestCaseSupplier typeErrorSupplier(List> validPerPosition, List types) { + protected static TestCaseSupplier typeErrorSupplier( + boolean includeOrdinal, + List> validPerPosition, + List types + ) { return new TestCaseSupplier( "type error for " + TestCaseSupplier.nameFromTypes(types), types, () -> TestCaseSupplier.TestCase.typeError( types.stream().map(type -> new TestCaseSupplier.TypedData(randomLiteral(type).value(), type, type.typeName())).toList(), - typeErrorMessage(validPerPosition, types) + typeErrorMessage(includeOrdinal, validPerPosition, types) ) ); } @@ -453,7 +464,7 @@ private static TestCaseSupplier typeErrorSupplier(List> validPerPo /** * Build the expected error message for an invalid type signature. */ - private static String typeErrorMessage(List> validPerPosition, List types) { + private static String typeErrorMessage(boolean includeOrdinal, List> validPerPosition, List types) { int badArgPosition = -1; for (int i = 0; i < types.size(); i++) { if (validPerPosition.get(i).contains(types.get(i)) == false) { @@ -464,9 +475,7 @@ private static String typeErrorMessage(List> validPerPosition, Lis if (badArgPosition == -1) { throw new IllegalStateException("can't find badArgPosition"); } - String ordinal = validPerPosition.size() == 1 - ? "" - : TypeResolutions.ParamOrdinal.fromIndex(badArgPosition).name().toLowerCase(Locale.ROOT) + " "; + String ordinal = includeOrdinal ? TypeResolutions.ParamOrdinal.fromIndex(badArgPosition).name().toLowerCase(Locale.ROOT) + " " : ""; String expectedType = expectedType(validPerPosition.get(badArgPosition)); String name = types.get(badArgPosition).typeName(); return ordinal + "argument of [] must be [" + expectedType + "], found value [" + name + "] type [" + name + "]"; @@ -477,6 +486,7 @@ private static String typeErrorMessage(List> validPerPosition, Lis Map.entry(Set.of(DataTypes.INTEGER, DataTypes.NULL), "integer"), Map.entry(Set.of(DataTypes.LONG, DataTypes.INTEGER, DataTypes.UNSIGNED_LONG, DataTypes.DOUBLE, DataTypes.NULL), "numeric"), Map.entry(Set.of(DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.VERSION, DataTypes.NULL), "keyword, text or version"), + Map.entry(Set.of(DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL), "string"), Map.entry(Set.of(DataTypes.IP, DataTypes.KEYWORD, DataTypes.NULL), "ip or keyword") ); @@ -614,13 +624,21 @@ private static void writeToTempDir(String subdir, String str, String extension) Files.writeString(file, str); } + private final List breakers = Collections.synchronizedList(new ArrayList<>()); + /** * A {@link DriverContext} with a BigArrays that does not circuit break. */ protected DriverContext driverContext() { - return new DriverContext( - new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new NoneCircuitBreakerService()).withCircuitBreaking(), - BlockFactory.getGlobalInstance() - ); + MockBigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, ByteSizeValue.ofGb(1)); + breakers.add(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST)); + return new DriverContext(bigArrays.withCircuitBreaking(), BlockFactory.getGlobalInstance()); + } + + @After + public void allMemoryReleased() { + for (CircuitBreaker breaker : breakers) { + assertThat(breaker.getUsed(), equalTo(0L)); + } } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java index 68cea4ea873a5..50c2e10558508 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -148,9 +149,15 @@ public void testCaseIsLazy() { assertEquals(1, toJavaObject(caseExpr.toEvaluator(child -> { Object value = child.fold(); if (value != null && value.equals(2)) { - return dvrCtx -> page -> { - fail("Unexpected evaluation of 4th argument"); - return null; + return dvrCtx -> new EvalOperator.ExpressionEvaluator() { + @Override + public Block eval(Page page) { + fail("Unexpected evaluation of 4th argument"); + return null; + } + + @Override + public void close() {} }; } return evaluator(child); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java index a70fbf45ab4fd..f6a41a042e68c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java @@ -11,6 +11,8 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.evaluator.EvalMapper; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; @@ -84,7 +86,15 @@ public void testCoalesceIsLazy() { Layout layout = builder.build(); assertThat(toJavaObject(exp.toEvaluator(child -> { if (child == evil) { - return dvrCtx -> page -> { throw new AssertionError("shouldn't be called"); }; + return dvrCtx -> new EvalOperator.ExpressionEvaluator() { + @Override + public Block eval(Page page) { + throw new AssertionError("shouldn't be called"); + } + + @Override + public void close() {} + }; } return EvalMapper.toEvaluator(child, layout); }).get(driverContext()).eval(row(testCase.getDataValues())), 0), testCase.getMatcher()); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java index 39d328747199d..3285b5848d311 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java @@ -11,71 +11,82 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; -import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractScalarFunctionTestCase; +import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; -import org.elasticsearch.xpack.ql.expression.Literal; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; -import org.hamcrest.Matcher; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.IntStream; -import java.util.stream.Stream; import static org.elasticsearch.compute.data.BlockUtils.toJavaObject; import static org.hamcrest.Matchers.equalTo; -public class ConcatTests extends AbstractScalarFunctionTestCase { +public class ConcatTests extends AbstractFunctionTestCase { public ConcatTests(@Name("TestCase") Supplier testCaseSupplier) { this.testCase = testCaseSupplier.get(); } @ParametersFactory public static Iterable parameters() { - return parameterSuppliersFromTypedData(List.of(new TestCaseSupplier("concat basic test", () -> { - BytesRef first = new BytesRef(randomAlphaOfLength(3)); - BytesRef second = new BytesRef(randomAlphaOfLength(3)); - return new TestCaseSupplier.TestCase( - List.of( - new TestCaseSupplier.TypedData(first, DataTypes.KEYWORD, "first"), - new TestCaseSupplier.TypedData(second, DataTypes.KEYWORD, "second") - ), - "ConcatEvaluator[values=[Attribute[channel=0], Attribute[channel=1]]]", - DataTypes.KEYWORD, - equalTo(new BytesRef(first.utf8ToString() + second.utf8ToString())) - ); - }))); - } + List suppliers = new ArrayList<>(); + suppliers(suppliers, 2); + suppliers(suppliers, 3); + suppliers = anyNullIsNull(true, suppliers); + for (int length = 4; length < 100; length++) { + suppliers(suppliers, length); + } + Set supported = Set.of(DataTypes.NULL, DataTypes.KEYWORD, DataTypes.TEXT); + List> supportedPerPosition = List.of(supported, supported); + for (DataType lhs : EsqlDataTypes.types()) { + if (lhs == DataTypes.NULL || EsqlDataTypes.isRepresentable(lhs) == false) { + continue; + } + for (DataType rhs : EsqlDataTypes.types()) { + if (rhs == DataTypes.NULL || EsqlDataTypes.isRepresentable(rhs) == false) { + continue; + } + boolean lhsIsString = lhs == DataTypes.KEYWORD || lhs == DataTypes.TEXT; + boolean rhsIsString = rhs == DataTypes.KEYWORD || rhs == DataTypes.TEXT; + if (lhsIsString && rhsIsString) { + continue; + } - @Override - protected DataType expectedType(List argTypes) { - return DataTypes.KEYWORD; + suppliers.add(typeErrorSupplier(false, supportedPerPosition, List.of(lhs, rhs))); + } + } + return parameterSuppliersFromTypedData(suppliers); } - private Matcher resultsMatcher(List simpleData) { - return equalTo(new BytesRef(simpleData.stream().map(o -> ((BytesRef) o.data()).utf8ToString()).collect(Collectors.joining()))); + private static void suppliers(List suppliers, int length) { + suppliers.add(supplier("ascii", DataTypes.KEYWORD, length, () -> randomAlphaOfLengthBetween(1, 10))); + suppliers.add(supplier("unicode", DataTypes.TEXT, length, () -> randomRealisticUnicodeOfLengthBetween(1, 10))); } - @Override - protected List argSpec() { - return List.of( - required(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()), - optional(strings()) - ); + private static TestCaseSupplier supplier(String name, DataType type, int length, Supplier valueSupplier) { + return new TestCaseSupplier(length + " " + name, IntStream.range(0, length).mapToObj(i -> type).toList(), () -> { + List values = new ArrayList<>(); + String expectedValue = ""; + String expectedToString = "ConcatEvaluator[values=["; + for (int v = 0; v < length; v++) { + String value = valueSupplier.get(); + values.add(new TestCaseSupplier.TypedData(new BytesRef(value), type, Integer.toString(v))); + expectedValue += value; + if (v != 0) { + expectedToString += ", "; + } + expectedToString += "Attribute[channel=" + v + "]"; + } + expectedToString += "]]"; + return new TestCaseSupplier.TestCase(values, expectedToString, DataTypes.KEYWORD, equalTo(new BytesRef(expectedValue))); + }); } @Override @@ -83,47 +94,31 @@ protected Expression build(Source source, List args) { return new Concat(source, args.get(0), args.subList(1, args.size())); } - @Override - protected Matcher badTypeError(List specs, int badArgPosition, DataType badArgType) { - return equalTo("argument of [exp] must be [string], found value [arg" + badArgPosition + "] type [" + badArgType.typeName() + "]"); - } - - public void testMany() { - List simpleData = Stream.of("cats", " ", "and", " ", "dogs").map(s -> (Object) new BytesRef(s)).toList(); - assertThat( - toJavaObject( - evaluator( - new Concat( - Source.EMPTY, - field("a", DataTypes.KEYWORD), - IntStream.range(1, 5).mapToObj(i -> field(Integer.toString(i), DataTypes.KEYWORD)).toList() - ) - ).get(driverContext()).eval(row(simpleData)), - 0 - ), - equalTo(new BytesRef("cats and dogs")) - ); - } - public void testSomeConstant() { - List simpleData = Stream.of("cats", "and", "dogs").map(s -> (Object) new BytesRef(s)).toList(); - assertThat( - toJavaObject( - evaluator( - new Concat( - Source.EMPTY, - field("a", DataTypes.KEYWORD), - List.of( - new Literal(Source.EMPTY, new BytesRef(" "), DataTypes.KEYWORD), - field("b", DataTypes.KEYWORD), - new Literal(Source.EMPTY, new BytesRef(" "), DataTypes.KEYWORD), - field("c", DataTypes.KEYWORD) - ) - ) - ).get(driverContext()).eval(row(simpleData)), - 0 - ), - equalTo(new BytesRef("cats and dogs")) - ); + List fields = testCase.getDataAsFields(); + List literals = testCase.getDataAsLiterals(); + List fieldValues = new ArrayList<>(); + List mix = new ArrayList<>(fields.size()); + assert fields.size() == literals.size(); + for (int i = 0; i < fields.size(); i++) { + if (randomBoolean()) { + fieldValues.add(testCase.getData().get(i).data()); + mix.add(fields.get(i)); + } else { + mix.add(literals.get(i)); + } + } + if (fieldValues.isEmpty()) { + fieldValues.add(new BytesRef("dummy")); + } + Expression expression = build(testCase.getSource(), mix); + if (testCase.getExpectedTypeError() != null) { + assertTrue("expected unresolved", expression.typeResolved().unresolved()); + assertThat(expression.typeResolved().message(), equalTo(testCase.getExpectedTypeError())); + return; + } + try (EvalOperator.ExpressionEvaluator eval = evaluator(expression).get(driverContext())) { + assertThat(toJavaObject(eval.eval(row(fieldValues)), 0), testCase.getMatcher()); + } } } From bacd10a219da225a42e62a661ccc0ef0e6658c24 Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:59:58 +0100 Subject: [PATCH 043/155] Fix block hash test and revert mistaken changes (#99821) This commit fixes block hash test and reverts mistaken changes from a previous commit. --- .../blockhash/BytesRefLongBlockHash.java | 1 - .../aggregation/blockhash/IntBlockHash.java | 2 -- .../aggregation/blockhash/LongBlockHash.java | 3 ++- .../aggregation/blockhash/BlockHashTests.java | 15 +++++++++++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java index 0c5b60f471f8c..50fd1bb7b0943 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java @@ -74,7 +74,6 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { } else { new AddBlock(block1, block2, addInput).add(); } - Releasables.closeExpectNoException(block1, block2); } public IntVector add(BytesRefVector vector1, LongVector vector2) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java index 7e5f3c94b91cb..4fcd9735f6158 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java @@ -20,7 +20,6 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.MultivalueDedupe; import org.elasticsearch.compute.operator.MultivalueDedupeInt; -import org.elasticsearch.core.Releasables; import java.util.BitSet; @@ -53,7 +52,6 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { } else { addInput.add(0, add(vector)); } - Releasables.closeExpectNoException(block); } private IntVector add(IntVector vector) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java index b8b66e2197b63..5e5b46ae6eda1 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; import org.elasticsearch.compute.aggregation.SeenGroupIds; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongArrayBlock; @@ -62,7 +63,7 @@ private IntVector add(LongVector vector) { for (int i = 0; i < vector.getPositionCount(); i++) { groups[i] = Math.toIntExact(hashOrdToGroupNullReserved(longHash.add(vector.getLong(i)))); } - return vector.blockFactory().newIntArrayVector(groups, groups.length); + return new IntArrayVector(groups, groups.length); } private IntBlock add(LongBlock block) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java index 620bb5ab5319a..d6345c6c0a453 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.HashAggregationOperator; import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; import org.junit.After; @@ -46,11 +47,14 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class BlockHashTests extends ESTestCase { - static final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); - static final BlockFactory blockFactory = BlockFactory.getInstance(breaker, BigArrays.NON_RECYCLING_INSTANCE); + final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + final BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); @ParametersFactory public static List params() { @@ -1189,4 +1193,11 @@ private void assertKeys(Block[] actualKeys, Object[][] expectedKeys) { } } } + + // A breaker service that always returns the given breaker for getBreaker(CircuitBreaker.REQUEST) + static CircuitBreakerService mockBreakerService(CircuitBreaker breaker) { + CircuitBreakerService breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return breakerService; + } } From 634a1222a5d6cacd1c044f63ca65e576d7d72e88 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 22 Sep 2023 17:32:36 +0100 Subject: [PATCH 044/155] Expose methods in RecoveryClusterStateDelayListeners (#99823) Followup to #99768, these package-private methods need to be public for this test utility class to be useful. --- .../recovery/RecoveryClusterStateDelayListeners.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java b/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java index ba73845c7f63a..c7c4d410e3ef1 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/recovery/RecoveryClusterStateDelayListeners.java @@ -50,11 +50,11 @@ public void close() { clusterStateBarriers.values().forEach(l -> l.onResponse(null)); } - void addCleanup(Runnable runnable) { + public void addCleanup(Runnable runnable) { cleanup.add(runnable); } - SubscribableListener getClusterStateDelayListener(long clusterStateVersion) { + public SubscribableListener getClusterStateDelayListener(long clusterStateVersion) { ESTestCase.assertThat(clusterStateVersion, greaterThanOrEqualTo(initialClusterStateVersion)); if (refCounted.tryIncRef()) { try { @@ -67,7 +67,7 @@ SubscribableListener getClusterStateDelayListener(long clusterStateVersion } } - void onStartRecovery() { + public void onStartRecovery() { Thread.yield(); ESTestCase.assertFalse(startRecoveryListener.isDone()); startRecoveryListener.onResponse(null); From 2cf4eadb9c5223561a8ce9f7ac8cb13ecc544763 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Fri, 22 Sep 2023 13:32:23 -0400 Subject: [PATCH 045/155] [buildkite] Add trigger for elasticsearch-pull-request-check-serverless-submodule (#99830) --- .buildkite/pull-requests.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.buildkite/pull-requests.json b/.buildkite/pull-requests.json index 6aed0610caa11..d79f02a733b91 100644 --- a/.buildkite/pull-requests.json +++ b/.buildkite/pull-requests.json @@ -12,9 +12,26 @@ "build_on_commit": true, "build_on_comment": true, "trigger_comment_regex": "buildkite\\W+elasticsearch-ci.+", - "labels": ["buildkite-opt-in"], + "labels": [ + "buildkite-opt-in" + ], "cancel_intermediate_builds": true, "cancel_intermediate_builds_on_comment": false + }, + { + "enabled": true, + "pipeline_slug": "elasticsearch-pull-request-check-serverless-submodule", + "allow_org_users": true, + "allowed_repo_permissions": [ + "admin", + "write" + ], + "set_commit_status": false, + "build_on_commit": true, + "build_on_comment": false, + "labels": [ + "test-update-serverless" + ] } ] } From ccdd85f784b564f5c590322c68c3898cae30a107 Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Sat, 23 Sep 2023 23:01:47 +0100 Subject: [PATCH 046/155] Update version range in jvm.options for the Panama Vector API (#99846) This commit updates the version range in jvm.options for the Panama Vector API. The range is updated from just a single value 20, to a range starting from 20 with no upper bound. This effectively enables the Panama Vector API for use in Lucene on all JDK versions greater than 19. --- distribution/src/config/jvm.options | 2 +- docs/changelog/99846.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/99846.yaml diff --git a/distribution/src/config/jvm.options b/distribution/src/config/jvm.options index 85c42d3f4aeeb..c5e905f461f45 100644 --- a/distribution/src/config/jvm.options +++ b/distribution/src/config/jvm.options @@ -56,7 +56,7 @@ # Leverages accelerated vector hardware instructions; removing this may # result in less optimal vector performance -20:--add-modules=jdk.incubator.vector +20-:--add-modules=jdk.incubator.vector ## heap dumps diff --git a/docs/changelog/99846.yaml b/docs/changelog/99846.yaml new file mode 100644 index 0000000000000..198b0b6f939ac --- /dev/null +++ b/docs/changelog/99846.yaml @@ -0,0 +1,5 @@ +pr: 99846 +summary: Update version range in `jvm.options` for the Panama Vector API +area: Vector Search +type: bug +issues: [] From db985e448e5b44d2ecea2ada3f125a26b750346c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Mon, 25 Sep 2023 08:23:50 +0200 Subject: [PATCH 047/155] Chunk SingleNodeShutdownStatus and ShutdownShardMigrationStatus (and related action) response (#99798) Moving SingleNodeShutdownStatus and ShutdownShardMigrationStatus to chunked response --- docs/changelog/99798.yaml | 7 ++ .../ShutdownShardMigrationStatus.java | 38 +++--- .../xcontent/ChunkedToXContentHelper.java | 12 ++ .../ChunkedToXContentHelperTests.java | 118 ++++++++++++++++++ .../shutdown/GetShutdownStatusAction.java | 28 ++--- .../shutdown/RestGetShutdownStatusAction.java | 4 +- .../shutdown/SingleNodeShutdownStatus.java | 52 ++++---- 7 files changed, 207 insertions(+), 52 deletions(-) create mode 100644 docs/changelog/99798.yaml create mode 100644 server/src/test/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelperTests.java diff --git a/docs/changelog/99798.yaml b/docs/changelog/99798.yaml new file mode 100644 index 0000000000000..bd8b9da71541d --- /dev/null +++ b/docs/changelog/99798.yaml @@ -0,0 +1,7 @@ +pr: 99798 +summary: Chunk `SingleNodeShutdownStatus` and `ShutdownShardMigrationStatus` (and + related action) response +area: Infra/Node Lifecycle +type: enhancement +issues: + - 99678 diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/ShutdownShardMigrationStatus.java b/server/src/main/java/org/elasticsearch/cluster/metadata/ShutdownShardMigrationStatus.java index 417d6eb14d0b0..ecc26d15d001f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/ShutdownShardMigrationStatus.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/ShutdownShardMigrationStatus.java @@ -12,18 +12,25 @@ import org.elasticsearch.TransportVersions; import org.elasticsearch.cluster.routing.allocation.ShardAllocationDecision; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.ChunkedToXContent; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.core.Nullable; -import org.elasticsearch.xcontent.ToXContentObject; +import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; import java.util.Objects; -public class ShutdownShardMigrationStatus implements Writeable, ToXContentObject { +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.endObject; +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.singleChunk; +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.startObject; + +public class ShutdownShardMigrationStatus implements Writeable, ChunkedToXContentObject { private static final TransportVersion ALLOCATION_DECISION_ADDED_VERSION = TransportVersions.V_7_16_0; public static final String NODE_ALLOCATION_DECISION_KEY = "node_allocation_decision"; @@ -79,22 +86,23 @@ public SingleNodeShutdownMetadata.Status getStatus() { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + startObject(), + singleChunk((builder, p) -> buildHeader(builder)), + Objects.nonNull(allocationDecision) + ? Iterators.concat(startObject(NODE_ALLOCATION_DECISION_KEY), allocationDecision.toXContentChunked(params), endObject()) + : Collections.emptyIterator(), + endObject() + ); + } + + private XContentBuilder buildHeader(XContentBuilder builder) throws IOException { builder.field("status", status); builder.field("shard_migrations_remaining", shardsRemaining); if (Objects.nonNull(explanation)) { builder.field("explanation", explanation); } - if (Objects.nonNull(allocationDecision)) { - builder.startObject(NODE_ALLOCATION_DECISION_KEY); - { - // This field might be huge, TODO add chunking support here - ChunkedToXContent.wrapAsToXContent(allocationDecision).toXContent(builder, params); - } - builder.endObject(); - } - builder.endObject(); return builder; } @@ -126,6 +134,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString((b, p) -> buildHeader(b), false, false); } } diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java index 692693f9e04c8..0a2b631f2b545 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelper.java @@ -74,6 +74,18 @@ public static Iterator field(String name, String value) { return Iterators.single(((builder, params) -> builder.field(name, value))); } + /** + * Creates an Iterator to serialize a named field where the value is represented by a chunked ToXContext. + * Chunked equivalent for {@code XContentBuilder field(String name, ToXContent value)} + * @param name name of the field + * @param value ChunkedToXContent value for this field (single value, object or array) + * @param params ToXContent params to propagate for XContent serialization + * @return Iterator composing field name and value serialization + */ + public static Iterator field(String name, ChunkedToXContentObject value, ToXContent.Params params) { + return Iterators.concat(Iterators.single((builder, innerParam) -> builder.field(name)), value.toXContentChunked(params)); + } + public static Iterator array(String name, Iterator contents) { return Iterators.concat(ChunkedToXContentHelper.startArray(name), contents, ChunkedToXContentHelper.endArray()); } diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelperTests.java b/server/src/test/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelperTests.java new file mode 100644 index 0000000000000..3e21a21fd1667 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/xcontent/ChunkedToXContentHelperTests.java @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.common.xcontent; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xcontent.ToXContent; + +import java.util.Iterator; +import java.util.function.IntFunction; + +import static org.elasticsearch.xcontent.ToXContent.EMPTY_PARAMS; +import static org.hamcrest.Matchers.equalTo; + +public class ChunkedToXContentHelperTests extends ESTestCase { + + public void testFieldWithInnerChunkedObject() { + + ToXContent innerXContent = (builder, p) -> { + builder.startObject(); + builder.field("field1", 10); + builder.field("field2", "aaa"); + builder.endObject(); + return builder; + }; + + ToXContent outerXContent = (builder, p) -> { + builder.field("field3", 10); + builder.field("field4", innerXContent); + return builder; + }; + + var expectedContent = Strings.toString(outerXContent); + + ChunkedToXContentObject innerChunkedContent = params -> Iterators.concat( + ChunkedToXContentHelper.startObject(), + ChunkedToXContentHelper.field("field1", 10), + ChunkedToXContentHelper.field("field2", "aaa"), + ChunkedToXContentHelper.endObject() + ); + + ChunkedToXContent outerChunkedContent = params -> Iterators.concat( + ChunkedToXContentHelper.field("field3", 10), + ChunkedToXContentHelper.field("field4", innerChunkedContent, EMPTY_PARAMS) + ); + + assertThat(Strings.toString(outerChunkedContent), equalTo(expectedContent)); + } + + public void testFieldWithInnerChunkedArray() { + + ToXContent innerXContent = (builder, p) -> { + builder.startArray(); + builder.value(10); + builder.value(20); + builder.endArray(); + return builder; + }; + + ToXContent outerXContent = (builder, p) -> { + builder.field("field3", 10); + builder.field("field4", innerXContent); + return builder; + }; + + var expectedContent = Strings.toString(outerXContent); + + IntFunction> value = v -> Iterators.single(((builder, p) -> builder.value(v))); + + ChunkedToXContentObject innerChunkedContent = params -> Iterators.concat( + ChunkedToXContentHelper.startArray(), + value.apply(10), + value.apply(20), + ChunkedToXContentHelper.endArray() + ); + + ChunkedToXContent outerChunkedContent = params -> Iterators.concat( + ChunkedToXContentHelper.field("field3", 10), + ChunkedToXContentHelper.field("field4", innerChunkedContent, EMPTY_PARAMS) + ); + + assertThat(Strings.toString(outerChunkedContent), equalTo(expectedContent)); + } + + public void testFieldWithInnerChunkedField() { + + ToXContent innerXContent = (builder, p) -> { + builder.value(10); + return builder; + }; + + ToXContent outerXContent = (builder, p) -> { + builder.field("field3", 10); + builder.field("field4", innerXContent); + return builder; + }; + + var expectedContent = Strings.toString(outerXContent); + + IntFunction> value = v -> Iterators.single(((builder, p) -> builder.value(v))); + + ChunkedToXContentObject innerChunkedContent = params -> Iterators.single((builder, p) -> builder.value(10)); + + ChunkedToXContent outerChunkedContent = params -> Iterators.concat( + ChunkedToXContentHelper.field("field3", 10), + ChunkedToXContentHelper.field("field4", innerChunkedContent, EMPTY_PARAMS) + ); + + assertThat(Strings.toString(outerChunkedContent), equalTo(expectedContent)); + } +} diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/GetShutdownStatusAction.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/GetShutdownStatusAction.java index ff71308cbb46a..75afc9e0c05c5 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/GetShutdownStatusAction.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/GetShutdownStatusAction.java @@ -12,16 +12,19 @@ import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; -import org.elasticsearch.xcontent.ToXContentObject; -import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.ToXContent; import java.io.IOException; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -80,7 +83,7 @@ public Task createTask(long id, String type, String action, TaskId parentTaskId, } } - public static class Response extends ActionResponse implements ToXContentObject { + public static class Response extends ActionResponse implements ChunkedToXContentObject { final List shutdownStatuses; public Response(List shutdownStatuses) { @@ -96,17 +99,14 @@ public List getShutdownStatuses() { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - { - builder.startArray("nodes"); - for (SingleNodeShutdownStatus nodeShutdownStatus : shutdownStatuses) { - nodeShutdownStatus.toXContent(builder, params); - } - builder.endArray(); - } - builder.endObject(); - return builder; + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + ChunkedToXContentHelper.startArray("nodes"), + Iterators.flatMap(shutdownStatuses.iterator(), status -> status.toXContentChunked(params)), + ChunkedToXContentHelper.endArray(), + ChunkedToXContentHelper.endObject() + ); } @Override diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/RestGetShutdownStatusAction.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/RestGetShutdownStatusAction.java index 9b8ed274d63a5..5d10f1a3d517e 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/RestGetShutdownStatusAction.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/RestGetShutdownStatusAction.java @@ -14,7 +14,7 @@ import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import java.util.List; @@ -40,7 +40,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).execute( GetShutdownStatusAction.INSTANCE, new GetShutdownStatusAction.Request(nodeIds), - new RestToXContentListener<>(channel) + new RestChunkedToXContentListener<>(channel) ); } } diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/SingleNodeShutdownStatus.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/SingleNodeShutdownStatus.java index dc222a97e0e4a..39bf9e78b3b01 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/SingleNodeShutdownStatus.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/SingleNodeShutdownStatus.java @@ -12,17 +12,24 @@ import org.elasticsearch.cluster.metadata.ShutdownShardMigrationStatus; import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.xcontent.ParseField; -import org.elasticsearch.xcontent.ToXContentObject; -import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.ToXContent; import java.io.IOException; +import java.util.Iterator; import java.util.Objects; -public class SingleNodeShutdownStatus implements Writeable, ToXContentObject { +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.endObject; +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.singleChunk; +import static org.elasticsearch.common.xcontent.ChunkedToXContentHelper.startObject; + +public class SingleNodeShutdownStatus implements Writeable, ChunkedToXContentObject { private final SingleNodeShutdownMetadata metadata; private final ShutdownShardMigrationStatus shardMigrationStatus; @@ -108,9 +115,8 @@ public String toString() { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - { + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat(startObject(), singleChunk((builder, p) -> { builder.field(SingleNodeShutdownMetadata.NODE_ID_FIELD.getPreferredName(), metadata.getNodeId()); builder.field(SingleNodeShutdownMetadata.TYPE_FIELD.getPreferredName(), metadata.getType()); builder.field(SingleNodeShutdownMetadata.REASON_FIELD.getPreferredName(), metadata.getReason()); @@ -126,20 +132,24 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws metadata.getStartedAtMillis() ); builder.field(STATUS.getPreferredName(), overallStatus()); - builder.field(SHARD_MIGRATION_FIELD.getPreferredName(), shardMigrationStatus); - builder.field(PERSISTENT_TASKS_FIELD.getPreferredName(), persistentTasksStatus); - builder.field(PLUGINS_STATUS.getPreferredName(), pluginsStatus); - if (metadata.getTargetNodeName() != null) { - builder.field(TARGET_NODE_NAME_FIELD.getPreferredName(), metadata.getTargetNodeName()); - } - if (metadata.getGracePeriod() != null) { - builder.timeField( - SingleNodeShutdownMetadata.GRACE_PERIOD_FIELD.getPreferredName(), - metadata.getGracePeriod().getStringRep() - ); - } - } - builder.endObject(); - return builder; + return builder; + }), + ChunkedToXContentHelper.field(SHARD_MIGRATION_FIELD.getPreferredName(), shardMigrationStatus, params), + singleChunk((builder, p) -> { + builder.field(PERSISTENT_TASKS_FIELD.getPreferredName(), persistentTasksStatus); + builder.field(PLUGINS_STATUS.getPreferredName(), pluginsStatus); + if (metadata.getTargetNodeName() != null) { + builder.field(TARGET_NODE_NAME_FIELD.getPreferredName(), metadata.getTargetNodeName()); + } + if (metadata.getGracePeriod() != null) { + builder.timeField( + SingleNodeShutdownMetadata.GRACE_PERIOD_FIELD.getPreferredName(), + metadata.getGracePeriod().getStringRep() + ); + } + return builder; + }), + endObject() + ); } } From bcdd7d5f42e94c1e8d0e7075c19ba5f327133a1a Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Mon, 25 Sep 2023 09:29:27 +0200 Subject: [PATCH 048/155] Set ParentAggregationBuilder not to support concurrent execution (#99809) --- .../join/aggregations/ChildrenIT.java | 16 ++++++++++++-- .../join/aggregations/ParentIT.java | 22 ++++++++++++++++++- .../ParentAggregationBuilder.java | 5 +++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java index afb7ad702de9c..49d3d04b3ee5c 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -9,6 +9,7 @@ import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.internal.Requests; @@ -43,7 +44,18 @@ public class ChildrenIT extends AbstractParentChildTestCase { - public void testChildrenAggs() throws Exception { + public void testSimpleChildrenAgg() { + final SearchRequestBuilder searchRequest = client().prepareSearch("test") + .setQuery(matchQuery("randomized", true)) + .addAggregation(children("to_comment", "comment")); + final SearchResponse searchResponse = searchRequest.get(); + long count = categoryToControl.values().stream().mapToLong(control -> control.commentIds.size()).sum(); + assertSearchResponse(searchResponse); + Children childrenAgg = searchResponse.getAggregations().get("to_comment"); + assertThat("Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", childrenAgg.getDocCount(), equalTo(count)); + } + + public void testChildrenAggs() { SearchResponse searchResponse = client().prepareSearch("test") .setQuery(matchQuery("randomized", true)) .addAggregation( @@ -86,7 +98,7 @@ public void testChildrenAggs() throws Exception { } } - public void testParentWithMultipleBuckets() throws Exception { + public void testParentWithMultipleBuckets() { SearchResponse searchResponse = client().prepareSearch("test") .setQuery(matchQuery("randomized", false)) .addAggregation( diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java index 5c409179f4e18..26a8d44759513 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java @@ -32,7 +32,27 @@ public class ParentIT extends AbstractParentChildTestCase { - public void testSimpleParentAgg() throws Exception { + public void testSimpleParentAgg() { + final SearchRequestBuilder searchRequest = client().prepareSearch("test") + .setSize(0) + .setQuery(matchQuery("randomized", true)) + .addAggregation(parent("to_article", "comment")); + SearchResponse searchResponse = searchRequest.get(); + + assertSearchResponse(searchResponse); + long articlesWithComment = articleToControl.values() + .stream() + .filter(parentControl -> parentControl.commentIds.isEmpty() == false) + .count(); + Parent parentAgg = searchResponse.getAggregations().get("to_article"); + assertThat( + "Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", + parentAgg.getDocCount(), + equalTo(articlesWithComment) + ); + } + + public void testSimpleParentAggWithSubAgg() { final SearchRequestBuilder searchRequest = client().prepareSearch("test") .setSize(10000) .setQuery(matchQuery("randomized", true)) diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentAggregationBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentAggregationBuilder.java index c5a285de70577..d608efcba9b83 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentAggregationBuilder.java @@ -90,6 +90,11 @@ public BucketCardinality bucketCardinality() { return BucketCardinality.ONE; } + @Override + public boolean supportsParallelCollection() { + return false; + } + @Override protected ValuesSourceAggregatorFactory innerBuild( AggregationContext context, From d411acecbcbd029ebadbfa533a07c70330d454c8 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Mon, 25 Sep 2023 18:30:21 +1000 Subject: [PATCH 049/155] Suppress this-escape warning for JDK21 (#99848) Adds @SuppressWarnings("this-escape") to all necessary places to that Elasticsearch can compile with -Werror on JDK21 No investigation has been done to determine whether any of the cases are a potential source of errors - we have simply suppressed all existing occurrences. Resolves: #99845 --- .../ssl/SslConfigurationLoaderTests.java | 1 + .../org/elasticsearch/tdigest/Centroid.java | 3 + .../timeseries/TimeSeriesAggregator.java | 1 + .../ArrayValuesSourceAggregationBuilder.java | 1 + .../mustache/CustomMustacheFactory.java | 1 + .../RestMultiSearchTemplateActionTests.java | 1 + .../RestSearchTemplateActionTests.java | 1 + .../index/rankeval/RankEvalRequest.java | 1 + .../TransportRankEvalActionTests.java | 1 + .../reindex/RestDeleteByQueryActionTests.java | 1 + .../reindex/RestUpdateByQueryActionTests.java | 1 + .../gcs/GoogleCloudStoragePlugin.java | 1 + .../analysis/phonetic/KoelnerPhonetik.java | 1 + .../discovery/ec2/Ec2DiscoveryPlugin.java | 1 + .../cloud/gce/GceInstancesServiceImpl.java | 1 + .../packaging/util/Packages.java | 1 + .../elasticsearch/ElasticsearchException.java | 1 + .../ResourceAlreadyExistsException.java | 1 + .../NoShardAvailableActionException.java | 4 ++ .../action/RoutingMissingException.java | 1 + .../cluster/node/info/NodesInfoRequest.java | 1 + .../shards/ClusterSearchShardsRequest.java | 1 + .../cluster/stats/ClusterStatsResponse.java | 1 + .../admin/indices/analyze/AnalyzeAction.java | 1 + ...TransportVerifyShardBeforeCloseAction.java | 1 + .../TransportVerifyShardIndexBlockAction.java | 1 + .../admin/indices/stats/CommonStatsFlags.java | 1 + .../validate/query/ValidateQueryRequest.java | 1 + .../action/bulk/BulkShardRequest.java | 1 + .../action/search/SearchRequest.java | 1 + ...roadcastShardOperationFailedException.java | 1 + .../broadcast/TransportBroadcastAction.java | 1 + .../node/TransportBroadcastByNodeAction.java | 1 + .../support/nodes/BaseNodesResponse.java | 1 + .../replication/ReplicationOperation.java | 2 + .../TransportReplicationAction.java | 2 + .../shard/TransportSingleShardAction.java | 1 + .../termvectors/TermVectorsRequest.java | 1 + .../action/update/UpdateResponse.java | 1 + .../cluster/InternalClusterInfoService.java | 1 + .../action/shard/ShardStateAction.java | 1 + .../cluster/coordination/Coordinator.java | 1 + .../coordination/FollowersChecker.java | 1 + .../coordination/InMemoryPersistedState.java | 1 + .../cluster/coordination/MasterHistory.java | 1 + .../cluster/coordination/Reconfigurator.java | 1 + .../metadata/IndexTemplateMetadata.java | 1 + .../cluster/metadata/MappingMetadata.java | 4 +- .../cluster/metadata/Metadata.java | 2 + .../metadata/TemplateUpgradeService.java | 1 + .../routing/DelayedAllocationService.java | 1 + .../cluster/routing/OperationRouting.java | 1 + .../routing/allocation/AllocationService.java | 1 + .../elasticsearch/common/geo/GeoPoint.java | 1 + .../common/inject/CreationException.java | 1 + .../io/stream/ByteArrayStreamInput.java | 2 + .../stream/VersionCheckingStreamOutput.java | 1 + .../common/logging/ECSJsonLayout.java | 1 + .../common/logging/ESJsonLayout.java | 1 + .../common/logging/ESLogMessage.java | 1 + .../common/metrics/Counters.java | 1 + .../settings/AbstractScopedSettings.java | 1 + .../settings/LocallyMountedSecrets.java | 1 + .../common/settings/Setting.java | 4 ++ .../common/util/BytesRefArray.java | 2 + .../common/util/LongObjectPagedHashMap.java | 1 + .../common/util/PlainIterator.java | 1 + .../elasticsearch/discovery/PeerFinder.java | 1 + .../elasticsearch/env/NodeEnvironment.java | 1 + .../env/ShardLockObtainFailedException.java | 2 + .../gateway/GatewayMetaState.java | 1 + .../org/elasticsearch/index/IndexService.java | 1 + .../index/codec/PerFieldMapperCodec.java | 1 + .../index/engine/EngineException.java | 1 + .../index/engine/InternalEngine.java | 2 + .../index/engine/ReadOnlyEngine.java | 1 + .../index/fieldvisitor/FieldsVisitor.java | 1 + .../index/mapper/BinaryFieldMapper.java | 1 + .../index/mapper/BooleanFieldMapper.java | 1 + .../index/mapper/ConstantFieldType.java | 1 + .../index/mapper/DateFieldMapper.java | 1 + .../index/mapper/GeoPointFieldMapper.java | 1 + .../index/mapper/IpFieldMapper.java | 1 + .../index/mapper/KeywordFieldMapper.java | 1 + .../index/mapper/MapperService.java | 1 + .../index/mapper/NumberFieldMapper.java | 1 + .../index/mapper/TextFieldMapper.java | 1 + .../index/query/AbstractQueryBuilder.java | 1 + .../query/CombinedFieldsQueryBuilder.java | 1 + .../index/query/MultiMatchQueryBuilder.java | 2 + .../index/query/QueryShardException.java | 1 + .../index/query/QueryStringQueryBuilder.java | 1 + .../index/query/SimpleQueryStringBuilder.java | 1 + .../functionscore/DecayFunctionBuilder.java | 1 + .../IllegalIndexShardStateException.java | 1 + .../elasticsearch/index/shard/IndexShard.java | 1 + .../shard/IndexShardRecoveryException.java | 1 + .../index/shard/ShardNotFoundException.java | 1 + .../snapshots/IndexShardRestoreException.java | 1 + .../IndexShardSnapshotException.java | 1 + .../BlobStoreIndexShardSnapshot.java | 1 + .../index/translog/Translog.java | 1 + .../index/translog/TranslogException.java | 1 + .../indices/AliasFilterParsingException.java | 1 + .../indices/IndexClosedException.java | 1 + .../indices/IndexCreationException.java | 1 + ...ndexPrimaryShardNotAllocatedException.java | 1 + .../elasticsearch/indices/IndicesService.java | 1 + .../indices/InvalidAliasNameException.java | 1 + .../indices/InvalidIndexNameException.java | 2 + .../elasticsearch/indices/SystemIndices.java | 1 + .../indices/TypeMissingException.java | 3 + .../indices/analysis/HunspellService.java | 1 + .../HierarchyCircuitBreakerService.java | 2 + .../cache/IndicesFieldDataCache.java | 1 + .../RecoverFilesRecoveryException.java | 1 + .../RecoveryCommitTooNewException.java | 1 + .../indices/recovery/RecoverySettings.java | 1 + .../indices/recovery/RecoveryTarget.java | 1 + .../indices/store/IndicesStore.java | 1 + .../ingest/CompoundProcessor.java | 2 + .../elasticsearch/ingest/IngestService.java | 1 + .../DeDuplicatingTokenFilter.java | 1 + .../uhighlight/CustomUnifiedHighlighter.java | 1 + .../monitor/fs/FsHealthService.java | 1 + .../java/org/elasticsearch/node/Node.java | 1 + .../PersistentTasksClusterService.java | 1 + .../decider/EnableAssignmentDecider.java | 1 + .../elasticsearch/plugins/PluginsService.java | 1 + .../repositories/RepositoriesService.java | 1 + .../repositories/SnapshotIndexCommit.java | 1 + .../blobstore/BlobStoreRepository.java | 1 + .../org/elasticsearch/rest/RestRequest.java | 2 + .../org/elasticsearch/rest/RestResponse.java | 1 + .../indices/AliasesNotFoundException.java | 1 + .../org/elasticsearch/script/Metadata.java | 1 + .../elasticsearch/script/ScriptService.java | 1 + .../script/field/WriteField.java | 1 + .../aggregations/AdaptingAggregator.java | 1 + .../search/aggregations/AggregatorBase.java | 1 + .../aggregations/AggregatorFactories.java | 1 + .../aggregations/AggregatorFactory.java | 1 + .../bucket/BucketsAggregator.java | 1 + .../bucket/geogrid/GeoGridAggregator.java | 1 + .../GeoHashGridAggregationBuilder.java | 1 + .../GeoTileGridAggregationBuilder.java | 1 + .../bucket/geogrid/InternalGeoGrid.java | 2 +- .../bucket/global/GlobalAggregator.java | 1 + .../AbstractHistogramAggregator.java | 1 + .../bucket/range/InternalRange.java | 1 + .../GlobalOrdinalsStringTermsAggregator.java | 1 + .../terms/MapStringTermsAggregator.java | 1 + .../bucket/terms/NumericTermsAggregator.java | 1 + .../bucket/terms/TermsAggregator.java | 1 + .../metrics/InternalCentroid.java | 1 + .../metrics/PercentilesConfig.java | 2 + .../metrics/ValueCountAggregator.java | 1 + .../MultiValuesSourceAggregationBuilder.java | 1 + .../ValuesSourceAggregationBuilder.java | 2 + .../support/ValuesSourceConfig.java | 1 + .../search/dfs/DfsSearchResult.java | 2 + .../search/fetch/FetchPhase.java | 1 + .../highlight/AbstractHighlighterBuilder.java | 1 + .../subphase/highlight/HighlightBuilder.java | 2 + .../search/internal/ContextIndexSearcher.java | 3 + .../search/internal/LegacyReaderContext.java | 1 + .../search/internal/ReaderContext.java | 1 + .../search/internal/ShardSearchRequest.java | 2 + .../search/sort/BucketedSort.java | 3 + .../search/sort/FieldSortBuilder.java | 1 + .../search/sort/ScoreSortBuilder.java | 2 + .../elasticsearch/search/suggest/Suggest.java | 5 +- .../InternalSnapshotsInfoService.java | 1 + .../snapshots/RestoreService.java | 1 + .../snapshots/SnapshotShardsService.java | 1 + .../snapshots/SnapshotsService.java | 1 + .../elasticsearch/transport/TcpTransport.java | 1 + .../transport/TransportService.java | 1 + .../index/mapper/ObjectMapperMergeTests.java | 1 + .../SkipStartingWithDigitTokenFilter.java | 1 + .../indices/RestGetIndicesActionTests.java | 1 + .../RestPutIndexTemplateActionTests.java | 1 + .../document/RestDeleteActionTests.java | 1 + .../action/document/RestGetActionTests.java | 1 + .../document/RestGetSourceActionTests.java | 1 + .../action/document/RestIndexActionTests.java | 1 + .../document/RestMultiGetActionTests.java | 2 + .../RestMultiTermVectorsActionTests.java | 1 + .../document/RestTermVectorsActionTests.java | 1 + .../document/RestUpdateActionTests.java | 1 + .../action/search/RestCountActionTests.java | 1 + .../action/search/RestExplainActionTests.java | 1 + .../search/RestMultiSearchActionTests.java | 1 + .../action/search/RestSearchActionTests.java | 1 + .../AbstractCoordinatorTestCase.java | 3 + .../CoordinationStateTestCluster.java | 1 + .../test/AbstractBootstrapCheckTestCase.java | 1 + .../elasticsearch/test/BackgroundIndexer.java | 1 + .../test/InternalAggregationTestCase.java | 2 + .../test/rest/RestActionTestCase.java | 1 + .../test/transport/MockTransport.java | 1 + .../local/DefaultLocalClusterSpecBuilder.java | 1 + .../HistoBackedHistogramAggregator.java | 1 + .../range/HistoBackedRangeAggregator.java | 1 + .../metrics/HistoBackedAvgAggregator.java | 1 + .../metrics/HistoBackedMaxAggregator.java | 1 + .../metrics/HistoBackedMinAggregator.java | 1 + .../metrics/HistoBackedSumAggregator.java | 1 + .../HistoBackedValueCountAggregator.java | 1 + .../rate/AbstractRateAggregator.java | 1 + .../rate/TimeSeriesRateAggregator.java | 1 + .../xpack/autoscaling/Autoscaling.java | 2 + .../ccr/action/ShardFollowTaskCleaner.java | 1 + .../ccr/action/ShardFollowTasksExecutor.java | 1 + .../license/ClusterStateLicenseService.java | 1 + .../action/AbstractGetResourcesResponse.java | 1 + .../core/action/TransportXPackInfoAction.java | 1 + .../action/TransportXPackUsageAction.java | 1 + .../xpack/core/async/StoredAsyncTask.java | 1 + .../core/ccr/action/FollowParameters.java | 1 + .../core/ccr/action/PutFollowAction.java | 1 + .../core/indexing/AsyncTwoPhaseIndexer.java | 1 + .../DeleteDataFrameAnalyticsAction.java | 1 + .../action/GetDataFrameAnalyticsAction.java | 2 + .../core/ml/action/GetDatafeedsAction.java | 1 + .../core/ml/action/GetFiltersAction.java | 2 + .../xpack/core/ml/action/GetJobsAction.java | 1 + .../ml/action/GetTrainedModelsAction.java | 1 + .../action/GetTrainedModelsStatsAction.java | 2 + .../action/StopDataFrameAnalyticsAction.java | 1 + .../classification/Classification.java | 1 + .../outlierdetection/OutlierDetection.java | 1 + .../evaluation/regression/Regression.java | 1 + .../assignment/TrainedModelAssignment.java | 1 + .../ml/inference/trainedmodel/tree/Tree.java | 1 + .../core/ml/job/config/AnalysisConfig.java | 1 + .../process/autodetect/state/DataCounts.java | 1 + .../ml/job/results/ForecastRequestStats.java | 1 + .../apikey/AbstractCreateApiKeyRequest.java | 1 + .../core/security/authc/RealmConfig.java | 1 + .../privilege/ActionClusterPrivilege.java | 1 + .../xpack/core/ssl/SSLService.java | 2 + .../xpack/core/ssl/SslSettingsLoader.java | 1 + .../core/template/IndexTemplateRegistry.java | 1 + .../termsenum/action/TermsEnumRequest.java | 2 + .../structurefinder/TextStructure.java | 1 + .../action/GetTransformStatsAction.java | 1 + .../action/ScheduleNowTransformAction.java | 1 + .../transform/action/StopTransformAction.java | 1 + .../action/UpdateTransformAction.java | 1 + .../transform/transforms/TransformConfig.java | 2 + .../transforms/TransformConfigUpdate.java | 1 + .../core/watcher/common/stats/Counters.java | 2 + .../watcher/transform/TransformRegistry.java | 1 + .../transform/chain/ChainTransform.java | 1 + .../AggregateMetricFieldValueFetcher.java | 1 + .../function/EqlFunctionRegistry.java | 1 + .../function/scalar/math/ToNumber.java | 1 + .../function/scalar/string/Between.java | 1 + .../function/scalar/string/IndexOf.java | 1 + .../function/scalar/string/Match.java | 2 + .../function/scalar/string/Substring.java | 1 + .../eql/plugin/TransportEqlSearchAction.java | 1 + .../operator/HashAggregationOperator.java | 1 + .../function/EsqlFunctionRegistry.java | 1 + .../function/UnsupportedAttribute.java | 1 + .../function/scalar/conditional/Case.java | 1 + .../xpack/esql/parser/EsqlBaseLexer.java | 2 +- .../xpack/esql/parser/EsqlBaseParser.java | 60 +++++++++---------- .../rest/action/RestGraphActionTests.java | 1 + .../xpack/ilm/IndexLifecycleService.java | 1 + .../AggregateDoubleMetricFieldMapper.java | 1 + .../unsignedlong/UnsignedLongFieldMapper.java | 1 + .../xpack/ml/MlInitializationService.java | 1 + .../MlAutoscalingDeciderService.java | 2 + .../monitoring/cleaner/CleanerService.java | 2 + .../xpack/monitoring/exporter/Exporters.java | 1 + .../exporter/local/LocalExporter.java | 1 + .../exporter/http/HttpResourceTests.java | 1 + .../http/NodeFailureListenerTests.java | 1 + .../http/WatcherExistsHttpResourceTests.java | 1 + ...ShardsOnInvalidLicenseClusterListener.java | 1 + .../Lucene60MetadataOnlyPointsReader.java | 1 + .../extractor/AbstractFieldHitExtractor.java | 1 + .../xpack/ql/expression/AttributeMap.java | 1 + .../xpack/ql/expression/ExpressionSet.java | 1 + .../ql/expression/UnresolvedAttribute.java | 1 + .../expression/function/FunctionRegistry.java | 2 + .../fulltext/StringQueryPredicate.java | 1 + .../xpack/ql/index/RemoteClusterResolver.java | 1 + ...tractTransportQlAsyncGetResultsAction.java | 1 + ...stractTransportQlAsyncGetStatusAction.java | 1 + .../xpack/ql/querydsl/query/ScriptQuery.java | 1 + .../xpack/security/authc/ApiKeyService.java | 1 + .../xpack/security/authc/Realms.java | 1 + .../xpack/security/LocalStateSecurity.java | 1 + .../security/UnstableLocalStateSecurity.java | 1 + .../support/DummyUsernamePasswordRealm.java | 1 + .../xpack/shutdown/NodeSeenService.java | 1 + .../xpack/slm/SlmHealthIndicatorService.java | 1 + .../geogrid/GeoHexGridAggregationBuilder.java | 1 + .../CartesianBoundsAggregatorBase.java | 1 + .../xpack/sql/qa/cli/EmbeddedCli.java | 1 + .../sql/client/ConnectionConfiguration.java | 1 + .../xpack/sql/proto/RequestInfo.java | 2 + .../xpack/sql/analysis/analyzer/Analyzer.java | 1 + .../function/SqlFunctionRegistry.java | 1 + .../xpack/sql/plan/logical/Pivot.java | 1 + .../xpack/sql/plugin/SqlPlugin.java | 1 + .../sql/plugin/TransportSqlQueryAction.java | 1 + .../transforms/TransformIndexer.java | 1 + .../transform/transforms/TransformTask.java | 1 + .../xpack/watcher/input/InputRegistry.java | 1 + .../notification/NotificationService.java | 1 + .../watcher/notification/WebhookService.java | 1 + .../notification/email/EmailService.java | 1 + .../attachment/ReportingAttachmentParser.java | 1 + .../notification/jira/JiraService.java | 1 + .../pagerduty/PagerDutyService.java | 1 + .../notification/slack/SlackService.java | 1 + .../trigger/schedule/support/DayTimes.java | 2 + .../trigger/schedule/support/MonthTimes.java | 1 + .../trigger/schedule/support/YearTimes.java | 1 + .../WatchExecutionContextMockBuilder.java | 1 + .../wildcard/mapper/WildcardFieldMapper.java | 1 + .../example/realm/CustomRoleMappingRealm.java | 1 + 326 files changed, 409 insertions(+), 35 deletions(-) diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java index 5980aca16a78f..61d42e5db7083 100644 --- a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java +++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java @@ -31,6 +31,7 @@ public class SslConfigurationLoaderTests extends ESTestCase { + @SuppressWarnings("this-escape") private final Path certRoot = getDataPath("/certs/ca1/ca.crt").getParent().getParent(); private Settings settings; diff --git a/libs/tdigest/src/main/java/org/elasticsearch/tdigest/Centroid.java b/libs/tdigest/src/main/java/org/elasticsearch/tdigest/Centroid.java index a09a0862c30af..fe9b1f673f715 100644 --- a/libs/tdigest/src/main/java/org/elasticsearch/tdigest/Centroid.java +++ b/libs/tdigest/src/main/java/org/elasticsearch/tdigest/Centroid.java @@ -40,16 +40,19 @@ private Centroid() { id = uniqueCount.getAndIncrement(); } + @SuppressWarnings("this-escape") public Centroid(double x) { this(); start(x, 1, uniqueCount.getAndIncrement()); } + @SuppressWarnings("this-escape") public Centroid(double x, long w) { this(); start(x, w, uniqueCount.getAndIncrement()); } + @SuppressWarnings("this-escape") public Centroid(double x, long w, int id) { this(); start(x, w, id); diff --git a/modules/aggregations/src/main/java/org/elasticsearch/aggregations/bucket/timeseries/TimeSeriesAggregator.java b/modules/aggregations/src/main/java/org/elasticsearch/aggregations/bucket/timeseries/TimeSeriesAggregator.java index 6930b0579a897..d5b796f3b93c5 100644 --- a/modules/aggregations/src/main/java/org/elasticsearch/aggregations/bucket/timeseries/TimeSeriesAggregator.java +++ b/modules/aggregations/src/main/java/org/elasticsearch/aggregations/bucket/timeseries/TimeSeriesAggregator.java @@ -32,6 +32,7 @@ public class TimeSeriesAggregator extends BucketsAggregator { private final boolean keyed; private final int size; + @SuppressWarnings("this-escape") public TimeSeriesAggregator( String name, AggregatorFactories factories, diff --git a/modules/aggregations/src/main/java/org/elasticsearch/aggregations/metric/ArrayValuesSourceAggregationBuilder.java b/modules/aggregations/src/main/java/org/elasticsearch/aggregations/metric/ArrayValuesSourceAggregationBuilder.java index b78707bbb1f86..d578aeff28011 100644 --- a/modules/aggregations/src/main/java/org/elasticsearch/aggregations/metric/ArrayValuesSourceAggregationBuilder.java +++ b/modules/aggregations/src/main/java/org/elasticsearch/aggregations/metric/ArrayValuesSourceAggregationBuilder.java @@ -41,6 +41,7 @@ protected LeafOnly(String name) { super(name); } + @SuppressWarnings("this-escape") protected LeafOnly(LeafOnly clone, Builder factoriesBuilder, Map metadata) { super(clone, factoriesBuilder, metadata); if (factoriesBuilder.count() > 0) { diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java index cb507d8a3c3ff..6a3ee1feb73fb 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java @@ -63,6 +63,7 @@ public class CustomMustacheFactory extends DefaultMustacheFactory { private final Encoder encoder; + @SuppressWarnings("this-escape") public CustomMustacheFactory(String mediaType) { super(); setObjectHandler(new CustomReflectionObjectHandler()); diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateActionTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateActionTests.java index f4bbda5fbfc96..c234ea58c7ea4 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateActionTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateActionTests.java @@ -24,6 +24,7 @@ import java.util.Map; public class RestMultiSearchTemplateActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestSearchTemplateActionTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestSearchTemplateActionTests.java index 7c999c56a5526..bc0a5f87e25d3 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestSearchTemplateActionTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/RestSearchTemplateActionTests.java @@ -22,6 +22,7 @@ import java.util.Map; public class RestSearchTemplateActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RankEvalRequest.java b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RankEvalRequest.java index d62e6f7426cab..15f9798abe88b 100644 --- a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RankEvalRequest.java +++ b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RankEvalRequest.java @@ -35,6 +35,7 @@ public class RankEvalRequest extends ActionRequest implements IndicesRequest.Rep private SearchType searchType = SearchType.DEFAULT; + @SuppressWarnings("this-escape") public RankEvalRequest(RankEvalSpec rankingEvaluationSpec, String[] indices) { this.rankingEvaluationSpec = Objects.requireNonNull(rankingEvaluationSpec, "ranking evaluation specification must not be null"); indices(indices); diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/TransportRankEvalActionTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/TransportRankEvalActionTests.java index 3838916c15c64..f99a22cbac6ef 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/TransportRankEvalActionTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/TransportRankEvalActionTests.java @@ -32,6 +32,7 @@ public class TransportRankEvalActionTests extends ESTestCase { + @SuppressWarnings("this-escape") private Settings settings = Settings.builder() .put("path.home", createTempDir().toString()) .put("node.name", "test-" + getTestName()) diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/RestDeleteByQueryActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/RestDeleteByQueryActionTests.java index 5f249978d76e1..8e1cfb309a671 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/RestDeleteByQueryActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/RestDeleteByQueryActionTests.java @@ -25,6 +25,7 @@ public class RestDeleteByQueryActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/RestUpdateByQueryActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/RestUpdateByQueryActionTests.java index 635efbb40f7f8..7222b5efe9c85 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/RestUpdateByQueryActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/RestUpdateByQueryActionTests.java @@ -25,6 +25,7 @@ public class RestUpdateByQueryActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java index 37120ec49f640..6acaff1801ffc 100644 --- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java +++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java @@ -30,6 +30,7 @@ public class GoogleCloudStoragePlugin extends Plugin implements RepositoryPlugin // package-private for tests final GoogleCloudStorageService storageService; + @SuppressWarnings("this-escape") public GoogleCloudStoragePlugin(final Settings settings) { this.storageService = createStorageService(); // eagerly load client settings so that secure settings are readable (not closed) diff --git a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/plugin/analysis/phonetic/KoelnerPhonetik.java b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/plugin/analysis/phonetic/KoelnerPhonetik.java index ee4ee232fd6b9..aed1a8cfec19a 100644 --- a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/plugin/analysis/phonetic/KoelnerPhonetik.java +++ b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/plugin/analysis/phonetic/KoelnerPhonetik.java @@ -46,6 +46,7 @@ public class KoelnerPhonetik implements StringEncoder { /** * Constructor for Kölner Phonetik */ + @SuppressWarnings("this-escape") public KoelnerPhonetik() { init(); } diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java index 602b6033ca359..08cf7ea559bf7 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/Ec2DiscoveryPlugin.java @@ -74,6 +74,7 @@ public Ec2DiscoveryPlugin(Settings settings) { this(settings, new AwsEc2ServiceImpl()); } + @SuppressWarnings("this-escape") protected Ec2DiscoveryPlugin(Settings settings, AwsEc2ServiceImpl ec2Service) { this.settings = settings; this.ec2Service = ec2Service; diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesServiceImpl.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesServiceImpl.java index 13a04c6b24f77..d4369429babf3 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesServiceImpl.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesServiceImpl.java @@ -113,6 +113,7 @@ public Collection instances() { private final boolean validateCerts; + @SuppressWarnings("this-escape") public GceInstancesServiceImpl(Settings settings) { this.settings = settings; this.validateCerts = GCE_VALIDATE_CERTIFICATES.get(settings); diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java index 3dc9e6188ccee..d1fefd425ae7f 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java @@ -318,6 +318,7 @@ public static class JournaldWrapper { * Create a new wrapper for Elasticsearch JournalD logs. * @param sh A shell with appropriate permissions. */ + @SuppressWarnings("this-escape") public JournaldWrapper(Shell sh) { this.sh = sh; clear(); diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index a84c9017859c8..260af26925deb 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -144,6 +144,7 @@ public ElasticsearchException(String msg, Throwable cause, Object... args) { super(LoggerMessageFormat.format(msg, args), cause); } + @SuppressWarnings("this-escape") public ElasticsearchException(StreamInput in) throws IOException { super(in.readOptionalString(), in.readException()); readStackTrace(this, in); diff --git a/server/src/main/java/org/elasticsearch/ResourceAlreadyExistsException.java b/server/src/main/java/org/elasticsearch/ResourceAlreadyExistsException.java index 0c2db0edaa866..e28a350088b5e 100644 --- a/server/src/main/java/org/elasticsearch/ResourceAlreadyExistsException.java +++ b/server/src/main/java/org/elasticsearch/ResourceAlreadyExistsException.java @@ -16,6 +16,7 @@ public class ResourceAlreadyExistsException extends ElasticsearchException { + @SuppressWarnings("this-escape") public ResourceAlreadyExistsException(Index index) { this("index {} already exists", index.toString()); setIndex(index); diff --git a/server/src/main/java/org/elasticsearch/action/NoShardAvailableActionException.java b/server/src/main/java/org/elasticsearch/action/NoShardAvailableActionException.java index 29df8ec55c9b1..bb4eb6c202b76 100644 --- a/server/src/main/java/org/elasticsearch/action/NoShardAvailableActionException.java +++ b/server/src/main/java/org/elasticsearch/action/NoShardAvailableActionException.java @@ -28,18 +28,22 @@ public static NoShardAvailableActionException forOnShardFailureWrapper(String ms return new NoShardAvailableActionException(null, msg, null, true); } + @SuppressWarnings("this-escape") public NoShardAvailableActionException(ShardId shardId) { this(shardId, null, null, false); } + @SuppressWarnings("this-escape") public NoShardAvailableActionException(ShardId shardId, String msg) { this(shardId, msg, null, false); } + @SuppressWarnings("this-escape") public NoShardAvailableActionException(ShardId shardId, String msg, Throwable cause) { this(shardId, msg, cause, false); } + @SuppressWarnings("this-escape") private NoShardAvailableActionException(ShardId shardId, String msg, Throwable cause, boolean onShardFailureWrapper) { super(msg, cause); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/action/RoutingMissingException.java b/server/src/main/java/org/elasticsearch/action/RoutingMissingException.java index ec43dfdb3fd7f..a90bc14f9ac8d 100644 --- a/server/src/main/java/org/elasticsearch/action/RoutingMissingException.java +++ b/server/src/main/java/org/elasticsearch/action/RoutingMissingException.java @@ -22,6 +22,7 @@ public class RoutingMissingException extends ElasticsearchException { private final String id; + @SuppressWarnings("this-escape") public RoutingMissingException(String index, String id) { super("routing is required for [" + index + "]/[" + id + "]"); Objects.requireNonNull(index, "index must not be null"); diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java index 42bdec20d358b..4e52116020be2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java @@ -42,6 +42,7 @@ public NodesInfoRequest(StreamInput in) throws IOException { * Get information from nodes based on the nodes ids specified. If none are passed, information * for all nodes will be returned. */ + @SuppressWarnings("this-escape") public NodesInfoRequest(String... nodesIds) { super(nodesIds); all(); diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java index 9c8f18d31413e..39205715dca8f 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java @@ -31,6 +31,7 @@ public class ClusterSearchShardsRequest extends MasterNodeReadRequest { phase1 = in.readBoolean(); } + @SuppressWarnings("this-escape") public ShardRequest(final ShardId shardId, final ClusterBlock clusterBlock, final boolean phase1, final TaskId parentTaskId) { super(shardId); this.clusterBlock = Objects.requireNonNull(clusterBlock); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java index c707a5d8bf6e0..aec5718b31a84 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java @@ -166,6 +166,7 @@ public static class ShardRequest extends ReplicationRequest { clusterBlock = new ClusterBlock(in); } + @SuppressWarnings("this-escape") public ShardRequest(final ShardId shardId, final ClusterBlock clusterBlock, final TaskId parentTaskId) { super(shardId); this.clusterBlock = Objects.requireNonNull(clusterBlock); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/CommonStatsFlags.java b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/CommonStatsFlags.java index ae4f20e420fca..bccc7a8f7e243 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/stats/CommonStatsFlags.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/stats/CommonStatsFlags.java @@ -40,6 +40,7 @@ public class CommonStatsFlags implements Writeable, Cloneable { /** * @param flags flags to set. If no flags are supplied, default flags will be set. */ + @SuppressWarnings("this-escape") public CommonStatsFlags(Flag... flags) { if (flags.length > 0) { clear(); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java index 2564f5eb13dc6..64505d76e26b8 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/ValidateQueryRequest.java @@ -65,6 +65,7 @@ public ValidateQueryRequest(StreamInput in) throws IOException { * Constructs a new validate request against the provided indices. No indices provided means it will * run against all indices. */ + @SuppressWarnings("this-escape") public ValidateQueryRequest(String... indices) { super(indices); indicesOptions(DEFAULT_INDICES_OPTIONS); diff --git a/server/src/main/java/org/elasticsearch/action/bulk/BulkShardRequest.java b/server/src/main/java/org/elasticsearch/action/bulk/BulkShardRequest.java index 7d7265b0734e6..f3473f274bf38 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/BulkShardRequest.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/BulkShardRequest.java @@ -35,6 +35,7 @@ public BulkShardRequest(StreamInput in) throws IOException { items = in.readArray(i -> i.readOptionalWriteable(inpt -> new BulkItemRequest(shardId, inpt)), BulkItemRequest[]::new); } + @SuppressWarnings("this-escape") public BulkShardRequest(ShardId shardId, RefreshPolicy refreshPolicy, BulkItemRequest[] items) { super(shardId); this.items = items; diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java index 55ad5a2522967..65ed3e104fa98 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchRequest.java @@ -149,6 +149,7 @@ public SearchRequest(String... indices) { /** * Constructs a new search request against the provided indices with the given search source. */ + @SuppressWarnings("this-escape") public SearchRequest(String[] indices, SearchSourceBuilder source) { this(); if (source == null) { diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastShardOperationFailedException.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastShardOperationFailedException.java index 622238d3589aa..9cfe0a1f1b992 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastShardOperationFailedException.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastShardOperationFailedException.java @@ -30,6 +30,7 @@ public BroadcastShardOperationFailedException(ShardId shardId, Throwable cause) this(shardId, "", cause); } + @SuppressWarnings("this-escape") public BroadcastShardOperationFailedException(ShardId shardId, String msg, Throwable cause) { super(msg, cause); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java index 6be22f28e8ad9..cd1d9ac293823 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java @@ -53,6 +53,7 @@ public abstract class TransportBroadcastAction< private final String transportShardAction; private final Executor executor; + @SuppressWarnings("this-escape") protected TransportBroadcastAction( String actionName, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java index 4eeb3af4810cc..194b4852c16d7 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java @@ -95,6 +95,7 @@ public TransportBroadcastByNodeAction( this(actionName, clusterService, transportService, actionFilters, indexNameExpressionResolver, request, executor, true); } + @SuppressWarnings("this-escape") public TransportBroadcastByNodeAction( String actionName, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/action/support/nodes/BaseNodesResponse.java b/server/src/main/java/org/elasticsearch/action/support/nodes/BaseNodesResponse.java index 46290fbffb8ca..986496bc78f6a 100644 --- a/server/src/main/java/org/elasticsearch/action/support/nodes/BaseNodesResponse.java +++ b/server/src/main/java/org/elasticsearch/action/support/nodes/BaseNodesResponse.java @@ -27,6 +27,7 @@ public abstract class BaseNodesResponse private final List nodes; private Map nodesMap; + @SuppressWarnings("this-escape") protected BaseNodesResponse(StreamInput in) throws IOException { super(in); clusterName = new ClusterName(in); diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java b/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java index 0b4d90a896c10..1f347ec2b8cac 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java @@ -662,10 +662,12 @@ public interface ReplicaResponse { } public static class RetryOnPrimaryException extends ElasticsearchException { + @SuppressWarnings("this-escape") public RetryOnPrimaryException(ShardId shardId, String msg) { this(shardId, msg, null); } + @SuppressWarnings("this-escape") RetryOnPrimaryException(ShardId shardId, String msg, Throwable cause) { super(msg, cause); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index 9adf4881a1ef6..411f23a0fc0ad 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -158,6 +158,7 @@ protected TransportReplicationAction( ); } + @SuppressWarnings("this-escape") protected TransportReplicationAction( Settings settings, String actionName, @@ -606,6 +607,7 @@ protected Releasable checkReplicaLimits(final ReplicaRequest request) { public static class RetryOnReplicaException extends ElasticsearchException { + @SuppressWarnings("this-escape") public RetryOnReplicaException(ShardId shardId, String msg) { super(msg); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java b/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java index f4a12cae6258d..995102a13e4d0 100644 --- a/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/single/shard/TransportSingleShardAction.java @@ -61,6 +61,7 @@ public abstract class TransportSingleShardAction(); this.currentTimeMillisSupplier = threadPool::relativeTimeInMillis; diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/Reconfigurator.java b/server/src/main/java/org/elasticsearch/cluster/coordination/Reconfigurator.java index e12397230e868..2378678ec04db 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/Reconfigurator.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/Reconfigurator.java @@ -54,6 +54,7 @@ public class Reconfigurator { private volatile boolean autoShrinkVotingConfiguration; + @SuppressWarnings("this-escape") public Reconfigurator(Settings settings, ClusterSettings clusterSettings) { autoShrinkVotingConfiguration = CLUSTER_AUTO_SHRINK_VOTING_CONFIGURATION.get(settings); clusterSettings.addSettingsUpdateConsumer(CLUSTER_AUTO_SHRINK_VOTING_CONFIGURATION, this::setAutoShrinkVotingConfiguration); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetadata.java index 31bfc937a1ed3..7a40d7fd774d1 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetadata.java @@ -248,6 +248,7 @@ public Builder(String name) { aliases = new HashMap<>(); } + @SuppressWarnings("this-escape") public Builder(IndexTemplateMetadata indexTemplateMetadata) { this.name = indexTemplateMetadata.name(); order(indexTemplateMetadata.order()); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MappingMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MappingMetadata.java index ab4a2ed662b56..b629ab5d5f710 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MappingMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MappingMetadata.java @@ -48,7 +48,7 @@ public MappingMetadata(DocumentMapper docMapper) { this.routingRequired = docMapper.routingFieldMapper().required(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "this-escape", "unchecked" }) public MappingMetadata(CompressedXContent mapping) { this.source = mapping; Map mappingMap = XContentHelper.convertToMap(mapping.compressedReference(), true).v2(); @@ -59,7 +59,7 @@ public MappingMetadata(CompressedXContent mapping) { this.routingRequired = routingRequired((Map) mappingMap.get(this.type)); } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "this-escape", "unchecked" }) public MappingMetadata(String type, Map mapping) { this.type = type; try { diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index a61761497da5b..64b234c8f5d2b 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -1727,6 +1727,7 @@ public static class Builder { private final Map mappingsByHash; + @SuppressWarnings("this-escape") public Builder() { this(Map.of(), 0); } @@ -1749,6 +1750,7 @@ public Builder() { this.reservedStateMetadata = new HashMap<>(metadata.reservedStateMetadata); } + @SuppressWarnings("this-escape") private Builder(Map mappingsByHash, int indexCountHint) { clusterUUID = UNKNOWN_CLUSTER_UUID; indices = ImmutableOpenMap.builder(indexCountHint); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java index 553a4f7baa592..6dfabc695e1d4 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java @@ -64,6 +64,7 @@ public class TemplateUpgradeService implements ClusterStateListener { private Map lastTemplateMetadata; + @SuppressWarnings("this-escape") public TemplateUpgradeService( Client client, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java b/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java index d02a1f1e973cf..f11bcc0aff3b4 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java @@ -129,6 +129,7 @@ private void submitUnbatchedTask(@SuppressWarnings("SameParameterValue") String clusterService.submitUnbatchedStateUpdateTask(source, task); } + @SuppressWarnings("this-escape") @Inject public DelayedAllocationService(ThreadPool threadPool, ClusterService clusterService, AllocationService allocationService) { this.threadPool = threadPool; diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java b/server/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java index 675fd49c340e4..b51364ebc2c84 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/OperationRouting.java @@ -44,6 +44,7 @@ public class OperationRouting { private boolean useAdaptiveReplicaSelection; private final boolean isStateless; + @SuppressWarnings("this-escape") public OperationRouting(Settings settings, ClusterSettings clusterSettings) { this.isStateless = DiscoveryNode.isStateless(settings); this.useAdaptiveReplicaSelection = USE_ADAPTIVE_REPLICA_SELECTION_SETTING.get(settings); diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java index 6101ca5291f63..32c99f5baba85 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/AllocationService.java @@ -78,6 +78,7 @@ public class AllocationService { private final ShardRoutingRoleStrategy shardRoutingRoleStrategy; // only for tests that use the GatewayAllocator as the unique ExistingShardsAllocator + @SuppressWarnings("this-escape") public AllocationService( AllocationDeciders allocationDeciders, GatewayAllocator gatewayAllocator, diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoPoint.java b/server/src/main/java/org/elasticsearch/common/geo/GeoPoint.java index 3ecc3e1ae3d4f..e6ed24dc7220a 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoPoint.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoPoint.java @@ -38,6 +38,7 @@ public GeoPoint() {} * * @param value String to create the point from */ + @SuppressWarnings("this-escape") public GeoPoint(String value) { this.resetFromString(value); } diff --git a/server/src/main/java/org/elasticsearch/common/inject/CreationException.java b/server/src/main/java/org/elasticsearch/common/inject/CreationException.java index dc5cbbb589883..f09248de947e9 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/CreationException.java +++ b/server/src/main/java/org/elasticsearch/common/inject/CreationException.java @@ -33,6 +33,7 @@ public class CreationException extends RuntimeException { /** * Creates a CreationException containing {@code messages}. */ + @SuppressWarnings("this-escape") public CreationException(Collection messages) { this.messages = messages; if (this.messages.isEmpty()) { diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/ByteArrayStreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/ByteArrayStreamInput.java index 08f4e3914bd70..c7e9a4abf2c57 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/ByteArrayStreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/ByteArrayStreamInput.java @@ -23,10 +23,12 @@ public class ByteArrayStreamInput extends StreamInput { private int pos; private int limit; + @SuppressWarnings("this-escape") public ByteArrayStreamInput() { reset(BytesRef.EMPTY_BYTES); } + @SuppressWarnings("this-escape") public ByteArrayStreamInput(byte[] bytes) { reset(bytes); } diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/VersionCheckingStreamOutput.java b/server/src/main/java/org/elasticsearch/common/io/stream/VersionCheckingStreamOutput.java index eda4a99283c4c..6a02bedcdf086 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/VersionCheckingStreamOutput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/VersionCheckingStreamOutput.java @@ -19,6 +19,7 @@ */ public class VersionCheckingStreamOutput extends StreamOutput { + @SuppressWarnings("this-escape") public VersionCheckingStreamOutput(TransportVersion version) { setTransportVersion(version); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/ECSJsonLayout.java b/server/src/main/java/org/elasticsearch/common/logging/ECSJsonLayout.java index 0555178bf5353..54b5749b797f7 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/ECSJsonLayout.java +++ b/server/src/main/java/org/elasticsearch/common/logging/ECSJsonLayout.java @@ -39,6 +39,7 @@ public static class Builder extends AbstractStringLayout.Builder @PluginAttribute("dataset") String dataset; + @SuppressWarnings("this-escape") public Builder() { setCharset(StandardCharsets.UTF_8); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/ESJsonLayout.java b/server/src/main/java/org/elasticsearch/common/logging/ESJsonLayout.java index 7a89eb0ec3fa8..fb7475e3cba53 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/ESJsonLayout.java +++ b/server/src/main/java/org/elasticsearch/common/logging/ESJsonLayout.java @@ -163,6 +163,7 @@ public static class Builder> extends AbstractS @PluginConfiguration private Configuration config; + @SuppressWarnings("this-escape") public Builder() { setCharset(StandardCharsets.UTF_8); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/ESLogMessage.java b/server/src/main/java/org/elasticsearch/common/logging/ESLogMessage.java index 2e07318e13379..93b231bea6f30 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/ESLogMessage.java +++ b/server/src/main/java/org/elasticsearch/common/logging/ESLogMessage.java @@ -27,6 +27,7 @@ public class ESLogMessage extends MapMessage { private final List arguments = new ArrayList<>(); private String messagePattern; + @SuppressWarnings("this-escape") public ESLogMessage(String messagePattern, Object... args) { super(new LinkedHashMap<>()); Collections.addAll(this.arguments, args); diff --git a/server/src/main/java/org/elasticsearch/common/metrics/Counters.java b/server/src/main/java/org/elasticsearch/common/metrics/Counters.java index 4d34e6489f205..665ed371955c6 100644 --- a/server/src/main/java/org/elasticsearch/common/metrics/Counters.java +++ b/server/src/main/java/org/elasticsearch/common/metrics/Counters.java @@ -32,6 +32,7 @@ public class Counters implements Writeable { private final ConcurrentMap counters = new ConcurrentHashMap<>(); + @SuppressWarnings("this-escape") public Counters(StreamInput in) throws IOException { int numCounters = in.readVInt(); for (int i = 0; i < numCounters; i++) { diff --git a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java index 5abac6d1af099..c3ad2d4f9a9d9 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java @@ -52,6 +52,7 @@ public abstract class AbstractScopedSettings { private final Setting.Property scope; private Settings lastSettingsApplied; + @SuppressWarnings("this-escape") protected AbstractScopedSettings(final Settings settings, final Set> settingsSet, final Setting.Property scope) { this.logger = LogManager.getLogger(this.getClass()); this.settings = settings; diff --git a/server/src/main/java/org/elasticsearch/common/settings/LocallyMountedSecrets.java b/server/src/main/java/org/elasticsearch/common/settings/LocallyMountedSecrets.java index 0b7799ae572a5..1ac3db3827eb4 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/LocallyMountedSecrets.java +++ b/server/src/main/java/org/elasticsearch/common/settings/LocallyMountedSecrets.java @@ -116,6 +116,7 @@ public class LocallyMountedSecrets implements SecureSettings { /** * Direct constructor to be used by the CLI */ + @SuppressWarnings("this-escape") public LocallyMountedSecrets(Environment environment) { var secretsDirPath = resolveSecretsDir(environment); var secretsFilePath = resolveSecretsFile(environment); diff --git a/server/src/main/java/org/elasticsearch/common/settings/Setting.java b/server/src/main/java/org/elasticsearch/common/settings/Setting.java index c9307d7563a52..f40534749f017 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/Setting.java +++ b/server/src/main/java/org/elasticsearch/common/settings/Setting.java @@ -173,6 +173,7 @@ public enum Property { Property.IndexSettingDeprecatedInV7AndRemovedInV8 ); + @SuppressWarnings("this-escape") private Setting( Key key, @Nullable Setting fallbackSetting, @@ -246,6 +247,7 @@ public Setting(Key key, Function defaultValue, Function defaultValue, @@ -317,6 +319,7 @@ public Setting( * @param validator a {@link Validator} for validating this setting * @param properties properties for this setting like scope, filtering... */ + @SuppressWarnings("this-escape") public Setting(String key, Setting fallbackSetting, Function parser, Validator validator, Property... properties) { this(new SimpleKey(key), fallbackSetting, fallbackSetting::getRaw, parser, validator, properties); } @@ -328,6 +331,7 @@ public Setting(String key, Setting fallbackSetting, Function parse * @param parser a parser that parses the string rep into a complex datatype. * @param properties properties for this setting like scope, filtering... */ + @SuppressWarnings("this-escape") public Setting(Key key, Setting fallbackSetting, Function parser, Property... properties) { this(key, fallbackSetting, fallbackSetting::getRaw, parser, v -> {}, properties); } diff --git a/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java b/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java index 856c342e52e1d..de061c7f314d6 100644 --- a/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java +++ b/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java @@ -32,6 +32,7 @@ public class BytesRefArray implements Accountable, Releasable, Writeable { private ByteArray bytes; private long size; + @SuppressWarnings("this-escape") public BytesRefArray(long capacity, BigArrays bigArrays) { this.bigArrays = bigArrays; boolean success = false; @@ -48,6 +49,7 @@ public BytesRefArray(long capacity, BigArrays bigArrays) { size = 0; } + @SuppressWarnings("this-escape") public BytesRefArray(StreamInput in, BigArrays bigArrays) throws IOException { this.bigArrays = bigArrays; // we allocate big arrays so we have to `close` if we fail here or we'll leak them. diff --git a/server/src/main/java/org/elasticsearch/common/util/LongObjectPagedHashMap.java b/server/src/main/java/org/elasticsearch/common/util/LongObjectPagedHashMap.java index d1cb6ee3c75e4..f54500a806cca 100644 --- a/server/src/main/java/org/elasticsearch/common/util/LongObjectPagedHashMap.java +++ b/server/src/main/java/org/elasticsearch/common/util/LongObjectPagedHashMap.java @@ -26,6 +26,7 @@ public LongObjectPagedHashMap(long capacity, BigArrays bigArrays) { this(capacity, DEFAULT_MAX_LOAD_FACTOR, bigArrays); } + @SuppressWarnings("this-escape") public LongObjectPagedHashMap(long capacity, float maxLoadFactor, BigArrays bigArrays) { super(capacity, maxLoadFactor, bigArrays); boolean success = false; diff --git a/server/src/main/java/org/elasticsearch/common/util/PlainIterator.java b/server/src/main/java/org/elasticsearch/common/util/PlainIterator.java index 36a5e26319116..1ed609ef959d3 100644 --- a/server/src/main/java/org/elasticsearch/common/util/PlainIterator.java +++ b/server/src/main/java/org/elasticsearch/common/util/PlainIterator.java @@ -20,6 +20,7 @@ public class PlainIterator implements Iterable, Countable { // that although nextOrNull might be called from different threads, it can never happen concurrently. private volatile int index; + @SuppressWarnings("this-escape") public PlainIterator(List elements) { this.elements = elements; reset(); diff --git a/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java b/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java index 46e8f5210e447..ec315f5200978 100644 --- a/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java @@ -94,6 +94,7 @@ public abstract class PeerFinder { private Optional leader = Optional.empty(); private volatile List lastResolvedAddresses = emptyList(); + @SuppressWarnings("this-escape") public PeerFinder( Settings settings, TransportService transportService, diff --git a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java index c76ac309bcc18..0f4de31f1e541 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -209,6 +209,7 @@ public NodeLock(final Logger logger, final Environment environment, final Checke * Tries to acquire a node lock for a node id, throws {@code IOException} if it is unable to acquire it * @param pathFunction function to check node path before attempt of acquiring a node lock */ + @SuppressWarnings("this-escape") public NodeLock( final Logger logger, final Environment environment, diff --git a/server/src/main/java/org/elasticsearch/env/ShardLockObtainFailedException.java b/server/src/main/java/org/elasticsearch/env/ShardLockObtainFailedException.java index ed8a50defe628..001b35ab11cbd 100644 --- a/server/src/main/java/org/elasticsearch/env/ShardLockObtainFailedException.java +++ b/server/src/main/java/org/elasticsearch/env/ShardLockObtainFailedException.java @@ -19,11 +19,13 @@ */ public class ShardLockObtainFailedException extends ElasticsearchException { + @SuppressWarnings("this-escape") public ShardLockObtainFailedException(ShardId shardId, String message) { super(buildMessage(shardId, message)); this.setShard(shardId); } + @SuppressWarnings("this-escape") public ShardLockObtainFailedException(ShardId shardId, String message, Throwable cause) { super(buildMessage(shardId, message), cause); this.setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java index 335b63d656b1a..686f03830257b 100644 --- a/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java +++ b/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java @@ -505,6 +505,7 @@ public static class LucenePersistedState implements PersistedState { private final AtomicReference persistenceWriter = new AtomicReference<>(); private boolean writeNextStateFully; + @SuppressWarnings("this-escape") public LucenePersistedState( PersistedClusterStateService persistedClusterStateService, long currentTerm, diff --git a/server/src/main/java/org/elasticsearch/index/IndexService.java b/server/src/main/java/org/elasticsearch/index/IndexService.java index 781ff035a79ff..b038db5ac379a 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexService.java +++ b/server/src/main/java/org/elasticsearch/index/IndexService.java @@ -160,6 +160,7 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust private final ValuesSourceRegistry valuesSourceRegistry; private Supplier documentParsingObserverSupplier; + @SuppressWarnings("this-escape") public IndexService( IndexSettings indexSettings, IndexCreationContext indexCreationContext, diff --git a/server/src/main/java/org/elasticsearch/index/codec/PerFieldMapperCodec.java b/server/src/main/java/org/elasticsearch/index/codec/PerFieldMapperCodec.java index d30a91d2ae4d0..2d1364de17471 100644 --- a/server/src/main/java/org/elasticsearch/index/codec/PerFieldMapperCodec.java +++ b/server/src/main/java/org/elasticsearch/index/codec/PerFieldMapperCodec.java @@ -49,6 +49,7 @@ public class PerFieldMapperCodec extends Lucene95Codec { : "PerFieldMapperCodec must subclass the latest lucene codec: " + Lucene.LATEST_CODEC; } + @SuppressWarnings("this-escape") public PerFieldMapperCodec(Mode compressionMode, MapperService mapperService, BigArrays bigArrays) { super(compressionMode); this.mapperService = mapperService; diff --git a/server/src/main/java/org/elasticsearch/index/engine/EngineException.java b/server/src/main/java/org/elasticsearch/index/engine/EngineException.java index 0017dcadad6cf..40d39cfb2dbc5 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/EngineException.java +++ b/server/src/main/java/org/elasticsearch/index/engine/EngineException.java @@ -20,6 +20,7 @@ public EngineException(ShardId shardId, String msg, Object... params) { this(shardId, msg, null, params); } + @SuppressWarnings("this-escape") public EngineException(ShardId shardId, String msg, Throwable cause, Object... params) { super(msg, cause, params); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index b296de8739c90..37fce6411af4f 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -221,10 +221,12 @@ public class InternalEngine extends Engine { protected static final String REAL_TIME_GET_REFRESH_SOURCE = "realtime_get"; protected static final String UNSAFE_VERSION_MAP_REFRESH_SOURCE = "unsafe_version_map"; + @SuppressWarnings("this-escape") public InternalEngine(EngineConfig engineConfig) { this(engineConfig, IndexWriter.MAX_DOCS, LocalCheckpointTracker::new); } + @SuppressWarnings("this-escape") InternalEngine(EngineConfig engineConfig, int maxDocs, BiFunction localCheckpointTrackerSupplier) { super(engineConfig); this.maxDocs = maxDocs; diff --git a/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java b/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java index 19345083bbc7b..cae59baf1dfbd 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java @@ -93,6 +93,7 @@ public class ReadOnlyEngine extends Engine { * @param requireCompleteHistory indicates whether this engine permits an incomplete history (i.e. LCP < MSN) * @param lazilyLoadSoftDeletes indicates whether this engine should load the soft-delete based liveDocs eagerly, or on first access */ + @SuppressWarnings("this-escape") public ReadOnlyEngine( EngineConfig config, SeqNoStats seqNoStats, diff --git a/server/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java b/server/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java index 8b04b2f3013ea..4789dcc131b89 100644 --- a/server/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java +++ b/server/src/main/java/org/elasticsearch/index/fieldvisitor/FieldsVisitor.java @@ -47,6 +47,7 @@ public FieldsVisitor(boolean loadSource) { this(loadSource, SourceFieldMapper.NAME); } + @SuppressWarnings("this-escape") public FieldsVisitor(boolean loadSource, String sourceFieldName) { this.loadSource = loadSource; this.sourceFieldName = sourceFieldName; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java index 98aaf3fbda596..0457a23d85105 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java @@ -195,6 +195,7 @@ public static class CustomBinaryDocValuesField extends CustomDocValuesField { private final List bytesList; + @SuppressWarnings("this-escape") public CustomBinaryDocValuesField(String name, byte[] bytes) { super(name); bytesList = new ArrayList<>(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java index f2383148c31ed..54961f2f489bf 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java @@ -93,6 +93,7 @@ public static class Builder extends FieldMapper.Builder { private final IndexVersion indexCreatedVersion; + @SuppressWarnings("this-escape") public Builder(String name, ScriptCompiler scriptCompiler, boolean ignoreMalformedByDefault, IndexVersion indexCreatedVersion) { super(name); this.scriptCompiler = Objects.requireNonNull(scriptCompiler); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java index 0fdb36ab10032..05d0f5614ae8c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java @@ -31,6 +31,7 @@ */ public abstract class ConstantFieldType extends MappedFieldType { + @SuppressWarnings("this-escape") public ConstantFieldType(String name, Map meta) { super(name, true, false, true, TextSearchInfo.SIMPLE_MATCH_WITHOUT_TERMS, meta); assert isSearchable(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java index f57b3229b8062..cd71c80cdb8ed 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java @@ -252,6 +252,7 @@ public static class Builder extends FieldMapper.Builder { private final IndexVersion indexCreatedVersion; private final ScriptCompiler scriptCompiler; + @SuppressWarnings("this-escape") public Builder( String name, Resolution resolution, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java index 4f96723235035..4cc5e41d86f0c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java @@ -94,6 +94,7 @@ public static class Builder extends FieldMapper.Builder { private final Parameter dimension; // can only support time_series_dimension: false private final IndexMode indexMode; // either STANDARD or TIME_SERIES + @SuppressWarnings("this-escape") public Builder( String name, ScriptCompiler scriptCompiler, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 49339c756bc7f..80810ee0d7ab4 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -88,6 +88,7 @@ public static class Builder extends FieldMapper.Builder { private final IndexVersion indexCreatedVersion; private final ScriptCompiler scriptCompiler; + @SuppressWarnings("this-escape") public Builder(String name, ScriptCompiler scriptCompiler, boolean ignoreMalformedByDefault, IndexVersion indexCreatedVersion) { super(name); this.scriptCompiler = Objects.requireNonNull(scriptCompiler); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java index 2dfd4cbfff0b5..1e74f90ed7393 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java @@ -184,6 +184,7 @@ public static class Builder extends FieldMapper.Builder { private final ScriptCompiler scriptCompiler; private final IndexVersion indexCreatedVersion; + @SuppressWarnings("this-escape") public Builder(String name, IndexAnalyzers indexAnalyzers, ScriptCompiler scriptCompiler, IndexVersion indexCreatedVersion) { super(name); this.indexAnalyzers = indexAnalyzers; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java index 13aaf1a93a4d2..deaac37508511 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -157,6 +157,7 @@ public MapperService( ); } + @SuppressWarnings("this-escape") public MapperService( Supplier clusterTransportVersion, IndexSettings indexSettings, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java index 32683fd6469bf..4df3b9cf02985 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -143,6 +143,7 @@ public static Builder docValuesOnly(String name, NumberType type, IndexVersion i return builder; } + @SuppressWarnings("this-escape") public Builder( String name, NumberType type, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java index dd1adaa3c49e7..07dab213c13bb 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -1130,6 +1130,7 @@ public Query existsQuery(SearchExecutionContext context) { private final SubFieldInfo prefixFieldInfo; private final SubFieldInfo phraseFieldInfo; + @SuppressWarnings("this-escape") protected TextFieldMapper( String simpleName, FieldType fieldType, diff --git a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java index 06ea1d7640f4f..53eae2e86c39b 100644 --- a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java @@ -61,6 +61,7 @@ protected AbstractQueryBuilder() { } + @SuppressWarnings("this-escape") protected AbstractQueryBuilder(StreamInput in) throws IOException { boost = in.readFloat(); checkNegativeBoost(boost); diff --git a/server/src/main/java/org/elasticsearch/index/query/CombinedFieldsQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/CombinedFieldsQueryBuilder.java index ee1eb5ec3d5d3..2533b5b611068 100644 --- a/server/src/main/java/org/elasticsearch/index/query/CombinedFieldsQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/CombinedFieldsQueryBuilder.java @@ -109,6 +109,7 @@ public class CombinedFieldsQueryBuilder extends AbstractQueryBuilder pluginAndModuleFeatures) { featureDescriptors = buildFeatureMap(pluginAndModuleFeatures); indexDescriptors = featureDescriptors.values() diff --git a/server/src/main/java/org/elasticsearch/indices/TypeMissingException.java b/server/src/main/java/org/elasticsearch/indices/TypeMissingException.java index 13db199403bef..c53b72cbe3803 100644 --- a/server/src/main/java/org/elasticsearch/indices/TypeMissingException.java +++ b/server/src/main/java/org/elasticsearch/indices/TypeMissingException.java @@ -18,16 +18,19 @@ public class TypeMissingException extends ElasticsearchException { + @SuppressWarnings("this-escape") public TypeMissingException(Index index, String... types) { super("type" + Arrays.toString(types) + " missing"); setIndex(index); } + @SuppressWarnings("this-escape") public TypeMissingException(Index index, Throwable cause, String... types) { super("type" + Arrays.toString(types) + " missing", cause); setIndex(index); } + @SuppressWarnings("this-escape") public TypeMissingException(String index, String... types) { super("type[" + Arrays.toString(types) + "] missing"); setIndex(index); diff --git a/server/src/main/java/org/elasticsearch/indices/analysis/HunspellService.java b/server/src/main/java/org/elasticsearch/indices/analysis/HunspellService.java index a6cce250fb6fd..36a89f4c0d407 100644 --- a/server/src/main/java/org/elasticsearch/indices/analysis/HunspellService.java +++ b/server/src/main/java/org/elasticsearch/indices/analysis/HunspellService.java @@ -89,6 +89,7 @@ public class HunspellService { private final Path hunspellDir; private final Function loadingFunction; + @SuppressWarnings("this-escape") public HunspellService(final Settings settings, final Environment env, final Map knownDictionaries) throws IOException { this.knownDictionaries = Collections.unmodifiableMap(knownDictionaries); diff --git a/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java b/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java index f19cadf91739e..86b6013895263 100644 --- a/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java +++ b/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java @@ -145,10 +145,12 @@ public class HierarchyCircuitBreakerService extends CircuitBreakerService { private final Function overLimitStrategyFactory; private volatile OverLimitStrategy overLimitStrategy; + @SuppressWarnings("this-escape") public HierarchyCircuitBreakerService(Settings settings, List customBreakers, ClusterSettings clusterSettings) { this(settings, customBreakers, clusterSettings, HierarchyCircuitBreakerService::createOverLimitStrategy); } + @SuppressWarnings("this-escape") HierarchyCircuitBreakerService( Settings settings, List customBreakers, diff --git a/server/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java b/server/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java index 8656ede444501..76de0d43b7f2e 100644 --- a/server/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java +++ b/server/src/main/java/org/elasticsearch/indices/fielddata/cache/IndicesFieldDataCache.java @@ -51,6 +51,7 @@ public class IndicesFieldDataCache implements RemovalListener cache; + @SuppressWarnings("this-escape") public IndicesFieldDataCache(Settings settings, IndexFieldDataCache.Listener indicesFieldDataCacheListener) { this.indicesFieldDataCacheListener = indicesFieldDataCacheListener; final long sizeInBytes = INDICES_FIELDDATA_CACHE_SIZE_KEY.get(settings).getBytes(); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverFilesRecoveryException.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverFilesRecoveryException.java index 6916d870766a0..1a40f7526240c 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverFilesRecoveryException.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverFilesRecoveryException.java @@ -24,6 +24,7 @@ public class RecoverFilesRecoveryException extends ElasticsearchException implem private final ByteSizeValue totalFilesSize; + @SuppressWarnings("this-escape") public RecoverFilesRecoveryException(ShardId shardId, int numberOfFiles, ByteSizeValue totalFilesSize, Throwable cause) { super("Failed to transfer [{}] files with total size of [{}]", cause, numberOfFiles, totalFilesSize); Objects.requireNonNull(totalFilesSize, "totalFilesSize must not be null"); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java index 732b627167701..d89a429dc853f 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryCommitTooNewException.java @@ -15,6 +15,7 @@ import java.io.IOException; public class RecoveryCommitTooNewException extends ElasticsearchException { + @SuppressWarnings("this-escape") public RecoveryCommitTooNewException(ShardId shardId, String message) { super(message); setShard(shardId); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java index 1199e6ba5503f..287521ae60f32 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java @@ -407,6 +407,7 @@ public Iterator> settings() { private final ByteSizeValue availableDiskReadBandwidth; private final ByteSizeValue availableDiskWriteBandwidth; + @SuppressWarnings("this-escape") public RecoverySettings(Settings settings, ClusterSettings clusterSettings) { this.retryDelayStateSync = INDICES_RECOVERY_RETRY_DELAY_STATE_SYNC_SETTING.get(settings); this.maxConcurrentFileChunks = INDICES_RECOVERY_MAX_CONCURRENT_FILE_CHUNKS_SETTING.get(settings); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java index 4f0d3b7d798cc..dda7203fa7b0e 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoveryTarget.java @@ -103,6 +103,7 @@ public class RecoveryTarget extends AbstractRefCounted implements RecoveryTarget * preventing the exhaustion of repository resources. * @param listener called when recovery is completed/failed */ + @SuppressWarnings("this-escape") public RecoveryTarget( IndexShard indexShard, DiscoveryNode sourceNode, diff --git a/server/src/main/java/org/elasticsearch/indices/store/IndicesStore.java b/server/src/main/java/org/elasticsearch/indices/store/IndicesStore.java index f949dd59e8968..6c32ebd491edd 100644 --- a/server/src/main/java/org/elasticsearch/indices/store/IndicesStore.java +++ b/server/src/main/java/org/elasticsearch/indices/store/IndicesStore.java @@ -88,6 +88,7 @@ public class IndicesStore implements ClusterStateListener, Closeable { private final TimeValue deleteShardTimeout; + @SuppressWarnings("this-escape") @Inject public IndicesStore( Settings settings, diff --git a/server/src/main/java/org/elasticsearch/ingest/CompoundProcessor.java b/server/src/main/java/org/elasticsearch/ingest/CompoundProcessor.java index ad4ed59617196..ff59a36ff7a46 100644 --- a/server/src/main/java/org/elasticsearch/ingest/CompoundProcessor.java +++ b/server/src/main/java/org/elasticsearch/ingest/CompoundProcessor.java @@ -39,10 +39,12 @@ public CompoundProcessor(Processor... processors) { this(false, List.of(processors), List.of()); } + @SuppressWarnings("this-escape") public CompoundProcessor(boolean ignoreFailure, List processors, List onFailureProcessors) { this(ignoreFailure, processors, onFailureProcessors, System::nanoTime); } + @SuppressWarnings("this-escape") CompoundProcessor( boolean ignoreFailure, List processors, diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestService.java b/server/src/main/java/org/elasticsearch/ingest/IngestService.java index 0a34a03d38ea4..3db40ef018f98 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestService.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestService.java @@ -172,6 +172,7 @@ public void onFailure(Exception e) { } } + @SuppressWarnings("this-escape") public IngestService( ClusterService clusterService, ThreadPool threadPool, diff --git a/server/src/main/java/org/elasticsearch/lucene/analysis/miscellaneous/DeDuplicatingTokenFilter.java b/server/src/main/java/org/elasticsearch/lucene/analysis/miscellaneous/DeDuplicatingTokenFilter.java index 8ea7d14d830b8..e51470edf5230 100644 --- a/server/src/main/java/org/elasticsearch/lucene/analysis/miscellaneous/DeDuplicatingTokenFilter.java +++ b/server/src/main/java/org/elasticsearch/lucene/analysis/miscellaneous/DeDuplicatingTokenFilter.java @@ -37,6 +37,7 @@ * be used to inspect the number of prior sightings when emitDuplicates is true) */ public class DeDuplicatingTokenFilter extends FilteringTokenFilter { + @SuppressWarnings("this-escape") private final DuplicateSequenceAttribute seqAtt = addAttribute(DuplicateSequenceAttribute.class); private final boolean emitDuplicates; static final MurmurHash3.Hash128 seed = new MurmurHash3.Hash128(); diff --git a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java index 3b857d4221523..838dff420777d 100644 --- a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java +++ b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java @@ -79,6 +79,7 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter { * offset source for it because it'd be super slow * @param weightMatchesEnabled whether the {@link HighlightFlag#WEIGHT_MATCHES} should be enabled */ + @SuppressWarnings("this-escape") public CustomUnifiedHighlighter( Builder builder, OffsetSource offsetSource, diff --git a/server/src/main/java/org/elasticsearch/monitor/fs/FsHealthService.java b/server/src/main/java/org/elasticsearch/monitor/fs/FsHealthService.java index 65c5087ed6064..16d44b572cf87 100644 --- a/server/src/main/java/org/elasticsearch/monitor/fs/FsHealthService.java +++ b/server/src/main/java/org/elasticsearch/monitor/fs/FsHealthService.java @@ -82,6 +82,7 @@ public class FsHealthService extends AbstractLifecycleComponent implements NodeH Setting.Property.Dynamic ); + @SuppressWarnings("this-escape") public FsHealthService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool, NodeEnvironment nodeEnv) { this.threadPool = threadPool; this.enabled = ENABLED_SETTING.get(settings); diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index a198fe0233d84..ad8ad68bf7650 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -350,6 +350,7 @@ public Node(Environment environment) { * @param forbidPrivateIndexSettings whether or not private index settings are forbidden when creating an index; this is used in the * test framework for tests that rely on being able to set private settings */ + @SuppressWarnings("this-escape") protected Node( final Environment initialEnvironment, final Function pluginServiceCtor, diff --git a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksClusterService.java b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksClusterService.java index e2d7d0c8366d8..38d67efa734b8 100644 --- a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksClusterService.java +++ b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksClusterService.java @@ -65,6 +65,7 @@ public class PersistentTasksClusterService implements ClusterStateListener, Clos private final PeriodicRechecker periodicRechecker; private final AtomicBoolean reassigningTasks = new AtomicBoolean(false); + @SuppressWarnings("this-escape") public PersistentTasksClusterService( Settings settings, PersistentTasksExecutorRegistry registry, diff --git a/server/src/main/java/org/elasticsearch/persistent/decider/EnableAssignmentDecider.java b/server/src/main/java/org/elasticsearch/persistent/decider/EnableAssignmentDecider.java index cc76e85abd5c9..ebabec42ef11b 100644 --- a/server/src/main/java/org/elasticsearch/persistent/decider/EnableAssignmentDecider.java +++ b/server/src/main/java/org/elasticsearch/persistent/decider/EnableAssignmentDecider.java @@ -41,6 +41,7 @@ public class EnableAssignmentDecider { private volatile Allocation enableAssignment; + @SuppressWarnings("this-escape") public EnableAssignmentDecider(final Settings settings, final ClusterSettings clusterSettings) { this.enableAssignment = CLUSTER_TASKS_ALLOCATION_ENABLE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer(CLUSTER_TASKS_ALLOCATION_ENABLE_SETTING, this::setEnableAssignment); diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java index 96f3eedde165c..e55e5d96aa532 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -130,6 +130,7 @@ record LoadedPlugin(PluginDescriptor descriptor, Plugin instance, ClassLoader lo * @param modulesDirectory The directory modules exist in, or null if modules should not be loaded from the filesystem * @param pluginsDirectory The directory plugins exist in, or null if plugins should not be loaded from the filesystem */ + @SuppressWarnings("this-escape") public PluginsService(Settings settings, Path configPath, Path modulesDirectory, Path pluginsDirectory) { this.settings = settings; this.configPath = configPath; diff --git a/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java b/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java index 23e380e7ffbb1..da9610a77a563 100644 --- a/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java +++ b/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java @@ -102,6 +102,7 @@ public class RepositoriesService extends AbstractLifecycleComponent implements C private final List> preRestoreChecks; + @SuppressWarnings("this-escape") public RepositoriesService( Settings settings, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/repositories/SnapshotIndexCommit.java b/server/src/main/java/org/elasticsearch/repositories/SnapshotIndexCommit.java index 75647eba2305f..43594aa6047e8 100644 --- a/server/src/main/java/org/elasticsearch/repositories/SnapshotIndexCommit.java +++ b/server/src/main/java/org/elasticsearch/repositories/SnapshotIndexCommit.java @@ -25,6 +25,7 @@ public class SnapshotIndexCommit extends AbstractRefCounted { private final Runnable releaseInitialRef; private final SubscribableListener completionListeners = new SubscribableListener<>(); + @SuppressWarnings("this-escape") public SnapshotIndexCommit(Engine.IndexCommitRef commitRef) { this.commitRef = commitRef; this.releaseInitialRef = new RunOnce(this::decRef); diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index bfa4cc5be7863..971382fde57bb 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -393,6 +393,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp * @param metadata The metadata for this repository including name and settings * @param clusterService ClusterService */ + @SuppressWarnings("this-escape") protected BlobStoreRepository( final RepositoryMetadata metadata, final NamedXContentRegistry namedXContentRegistry, diff --git a/server/src/main/java/org/elasticsearch/rest/RestRequest.java b/server/src/main/java/org/elasticsearch/rest/RestRequest.java index 2bed0e2702cc2..eac13e5ef87a6 100644 --- a/server/src/main/java/org/elasticsearch/rest/RestRequest.java +++ b/server/src/main/java/org/elasticsearch/rest/RestRequest.java @@ -74,6 +74,7 @@ public boolean isContentConsumed() { return contentConsumed; } + @SuppressWarnings("this-escape") protected RestRequest( XContentParserConfiguration parserConfig, Map params, @@ -85,6 +86,7 @@ protected RestRequest( this(parserConfig, params, path, headers, httpRequest, httpChannel, requestIdGenerator.incrementAndGet()); } + @SuppressWarnings("this-escape") private RestRequest( XContentParserConfiguration parserConfig, Map params, diff --git a/server/src/main/java/org/elasticsearch/rest/RestResponse.java b/server/src/main/java/org/elasticsearch/rest/RestResponse.java index f99e8cbbf2026..3a82a827e3726 100644 --- a/server/src/main/java/org/elasticsearch/rest/RestResponse.java +++ b/server/src/main/java/org/elasticsearch/rest/RestResponse.java @@ -107,6 +107,7 @@ public RestResponse(RestChannel channel, Exception e) throws IOException { this(channel, ExceptionsHelper.status(e), e); } + @SuppressWarnings("this-escape") public RestResponse(RestChannel channel, RestStatus status, Exception e) throws IOException { this.status = status; ToXContent.Params params = paramsFromRequest(channel.request()); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/AliasesNotFoundException.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/AliasesNotFoundException.java index 7e1cdabb78ff4..4ebe5350e055b 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/AliasesNotFoundException.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/AliasesNotFoundException.java @@ -15,6 +15,7 @@ public class AliasesNotFoundException extends ResourceNotFoundException { + @SuppressWarnings("this-escape") public AliasesNotFoundException(String... names) { super("aliases " + Arrays.toString(names) + " missing"); this.setResources("aliases", names); diff --git a/server/src/main/java/org/elasticsearch/script/Metadata.java b/server/src/main/java/org/elasticsearch/script/Metadata.java index 7c9679507d5d4..75ea3cc7d7e80 100644 --- a/server/src/main/java/org/elasticsearch/script/Metadata.java +++ b/server/src/main/java/org/elasticsearch/script/Metadata.java @@ -72,6 +72,7 @@ public class Metadata { * @param map the backing map for this metadata instance * @param properties the immutable map of defined properties for the type of metadata represented by this instance */ + @SuppressWarnings("this-escape") protected Metadata(Map map, Map> properties) { this.map = map; // we can't tell the compiler that properties must be a java.util.ImmutableCollections.AbstractImmutableMap, but diff --git a/server/src/main/java/org/elasticsearch/script/ScriptService.java b/server/src/main/java/org/elasticsearch/script/ScriptService.java index b58d3fd972d6c..43c7cbb78f869 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptService.java @@ -185,6 +185,7 @@ public class ScriptService implements Closeable, ClusterStateApplier, ScriptComp // package private for tests final AtomicReference cacheHolder = new AtomicReference<>(); + @SuppressWarnings("this-escape") public ScriptService( Settings settings, Map engines, diff --git a/server/src/main/java/org/elasticsearch/script/field/WriteField.java b/server/src/main/java/org/elasticsearch/script/field/WriteField.java index a634319c083bb..6a50434b4004a 100644 --- a/server/src/main/java/org/elasticsearch/script/field/WriteField.java +++ b/server/src/main/java/org/elasticsearch/script/field/WriteField.java @@ -32,6 +32,7 @@ public class WriteField implements Field { private static final Object MISSING = new Object(); + @SuppressWarnings("this-escape") public WriteField(String path, Supplier> rootSupplier) { this.path = path; this.rootSupplier = rootSupplier; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AdaptingAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/AdaptingAggregator.java index f977e54d3e09e..0be4e7f729bbf 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AdaptingAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AdaptingAggregator.java @@ -26,6 +26,7 @@ public abstract class AdaptingAggregator extends Aggregator { private final Aggregator parent; private final Aggregator delegate; + @SuppressWarnings("this-escape") public AdaptingAggregator( Aggregator parent, AggregatorFactories subAggregators, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorBase.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorBase.java index c1f096ea782d2..be109b2909bcc 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorBase.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorBase.java @@ -58,6 +58,7 @@ public abstract class AggregatorBase extends Aggregator { * @param subAggregatorCardinality Upper bound of the number of buckets that sub aggregations will collect * @param metadata The metadata associated with this aggregator */ + @SuppressWarnings("this-escape") protected AggregatorBase( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java index e571458703e5a..1d12b33390659 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java @@ -290,6 +290,7 @@ public Builder() {} /** * Read from a stream. */ + @SuppressWarnings("this-escape") public Builder(StreamInput in) throws IOException { int factoriesSize = in.readVInt(); for (int i = 0; i < factoriesSize; i++) { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactory.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactory.java index 82a726dd54ab9..bf74494e872bb 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactory.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactory.java @@ -33,6 +33,7 @@ public abstract class AggregatorFactory { * @throws IOException * if an error occurs creating the factory */ + @SuppressWarnings("this-escape") public AggregatorFactory( String name, AggregationContext context, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/BucketsAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/BucketsAggregator.java index 6997d1f96c907..e2aca8c654b23 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/BucketsAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/BucketsAggregator.java @@ -44,6 +44,7 @@ public abstract class BucketsAggregator extends AggregatorBase { protected final DocCountProvider docCountProvider; private int callCount; + @SuppressWarnings("this-escape") public BucketsAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java index cc5e666096e59..21c245a0237f2 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java @@ -42,6 +42,7 @@ public abstract class GeoGridAggregator> extends Bu protected final ValuesSource.Numeric valuesSource; protected final LongKeyedBucketOrds bucketOrds; + @SuppressWarnings("this-escape") protected GeoGridAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java index 9fd1e3a393046..d51f14a516bc1 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java @@ -41,6 +41,7 @@ public class GeoHashGridAggregationBuilder extends GeoGridAggregationBuilder { GeoHashGridAggregationBuilder::new ); + @SuppressWarnings("this-escape") public GeoHashGridAggregationBuilder(String name) { super(name); precision(DEFAULT_PRECISION); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java index 2ee29c8a049ef..76286fc1605a2 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java @@ -40,6 +40,7 @@ public class GeoTileGridAggregationBuilder extends GeoGridAggregationBuilder { GeoTileGridAggregationBuilder::new ); + @SuppressWarnings("this-escape") public GeoTileGridAggregationBuilder(String name) { super(name); precision(DEFAULT_PRECISION); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java index f09dca2045d56..315eda4793a12 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java @@ -51,7 +51,7 @@ protected InternalGeoGrid(String name, int requiredSize, List metadata) throws IOException { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AbstractHistogramAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AbstractHistogramAggregator.java index ba4a9ed2cdab4..62b7a0747ca00 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AbstractHistogramAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/AbstractHistogramAggregator.java @@ -44,6 +44,7 @@ public abstract class AbstractHistogramAggregator extends BucketsAggregator { protected final DoubleBounds hardBounds; protected final LongKeyedBucketOrds bucketOrds; + @SuppressWarnings("this-escape") public AbstractHistogramAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java index f125e19c75b48..c6f3cbaf740f0 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java @@ -261,6 +261,7 @@ public InternalRange(String name, List ranges, DocValueFormat format, boolean /** * Read from a stream. */ + @SuppressWarnings("this-escape") public InternalRange(StreamInput in) throws IOException { super(in); format = in.readNamedWriteable(DocValueFormat.class); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java index 070334d9287d1..8948fc64ca411 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/GlobalOrdinalsStringTermsAggregator.java @@ -68,6 +68,7 @@ public interface GlobalOrdLookupFunction { BytesRef apply(long ord) throws IOException; } + @SuppressWarnings("this-escape") public GlobalOrdinalsStringTermsAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/MapStringTermsAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/MapStringTermsAggregator.java index ecc306a8c39a9..a26507413128c 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/MapStringTermsAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/MapStringTermsAggregator.java @@ -53,6 +53,7 @@ public class MapStringTermsAggregator extends AbstractStringTermsAggregator { private final BytesKeyedBucketOrds bucketOrds; private final IncludeExclude.StringFilter includeExclude; + @SuppressWarnings("this-escape") public MapStringTermsAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/NumericTermsAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/NumericTermsAggregator.java index d71528e5f29a7..b0d60962300b7 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/NumericTermsAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/NumericTermsAggregator.java @@ -51,6 +51,7 @@ public class NumericTermsAggregator extends TermsAggregator { private final LongKeyedBucketOrds bucketOrds; private final LongFilter longFilter; + @SuppressWarnings("this-escape") public NumericTermsAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java index 52385a5ba0b30..bbc6fb8123b5d 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregator.java @@ -193,6 +193,7 @@ public boolean equals(Object obj) { protected final Set aggsUsedForSorting; protected final SubAggCollectionMode collectMode; + @SuppressWarnings("this-escape") public TermsAggregator( String name, AggregatorFactories factories, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCentroid.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCentroid.java index 5e4bd11feae88..d7584b1c6eaf7 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCentroid.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalCentroid.java @@ -56,6 +56,7 @@ public InternalCentroid( /** * Read from a stream. */ + @SuppressWarnings("this-escape") protected InternalCentroid(StreamInput in, FieldExtractor firstField, FieldExtractor secondField) throws IOException { super(in); count = in.readVLong(); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/PercentilesConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/PercentilesConfig.java index ae20e3efa6208..92ff5cfb09c08 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/PercentilesConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/PercentilesConfig.java @@ -134,6 +134,7 @@ public TDigest(double compression) { this(compression, null); } + @SuppressWarnings("this-escape") public TDigest(double compression, TDigestExecutionHint executionHint) { super(PercentilesMethod.TDIGEST); this.executionHint = executionHint; @@ -288,6 +289,7 @@ public Hdr() { this(DEFAULT_NUMBER_SIG_FIGS); } + @SuppressWarnings("this-escape") public Hdr(int numberOfSignificantValueDigits) { super(PercentilesMethod.HDR); setNumberOfSignificantValueDigits(numberOfSignificantValueDigits); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ValueCountAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ValueCountAggregator.java index 6a304622a1413..e0c11530541ef 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ValueCountAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ValueCountAggregator.java @@ -38,6 +38,7 @@ public class ValueCountAggregator extends NumericMetricsAggregator.SingleValue { // a count per bucket LongArray counts; + @SuppressWarnings("this-escape") public ValueCountAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java index b0a5541f54a6e..4472083060d6e 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java @@ -40,6 +40,7 @@ protected LeafOnly(String name) { super(name); } + @SuppressWarnings("this-escape") protected LeafOnly(LeafOnly clone, Builder factoriesBuilder, Map metadata) { super(clone, factoriesBuilder, metadata); if (factoriesBuilder.count() > 0) { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java index e953a4c85e779..c1b9b8c376a59 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java @@ -115,6 +115,7 @@ protected LeafOnly(String name) { super(name); } + @SuppressWarnings("this-escape") protected LeafOnly(LeafOnly clone, Builder factoriesBuilder, Map metadata) { super(clone, factoriesBuilder, metadata); if (factoriesBuilder.count() > 0) { @@ -215,6 +216,7 @@ protected ValuesSourceAggregationBuilder( /** * Read from a stream. */ + @SuppressWarnings("this-escape") protected ValuesSourceAggregationBuilder(StreamInput in) throws IOException { super(in); if (serializeTargetValueType(in.getTransportVersion())) { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index 5f16a953f802f..028e2b922ee05 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -281,6 +281,7 @@ private ValuesSourceConfig() { throw new UnsupportedOperationException(); } + @SuppressWarnings("this-escape") public ValuesSourceConfig( ValuesSourceType valuesSourceType, FieldContext fieldContext, diff --git a/server/src/main/java/org/elasticsearch/search/dfs/DfsSearchResult.java b/server/src/main/java/org/elasticsearch/search/dfs/DfsSearchResult.java index 1a7b7b616021f..e72a0361e9dba 100644 --- a/server/src/main/java/org/elasticsearch/search/dfs/DfsSearchResult.java +++ b/server/src/main/java/org/elasticsearch/search/dfs/DfsSearchResult.java @@ -37,6 +37,7 @@ public class DfsSearchResult extends SearchPhaseResult { private int maxDoc; private SearchProfileDfsPhaseResult searchProfileDfsPhaseResult; + @SuppressWarnings("this-escape") public DfsSearchResult(StreamInput in) throws IOException { super(in); contextId = new ShardSearchContextId(in); @@ -69,6 +70,7 @@ public DfsSearchResult(StreamInput in) throws IOException { } } + @SuppressWarnings("this-escape") public DfsSearchResult(ShardSearchContextId contextId, SearchShardTarget shardTarget, ShardSearchRequest shardSearchRequest) { this.setSearchShardTarget(shardTarget); this.contextId = contextId; diff --git a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java index 8c319f5330ae9..2aeb36d75de62 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java @@ -51,6 +51,7 @@ public class FetchPhase { private final FetchSubPhase[] fetchSubPhases; + @SuppressWarnings("this-escape") public FetchPhase(List fetchSubPhases) { this.fetchSubPhases = fetchSubPhases.toArray(new FetchSubPhase[fetchSubPhases.size() + 1]); this.fetchSubPhases[fetchSubPhases.size()] = new InnerHitsPhase(this); diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java index 3ce5290b12100..3207f1ffa99f0 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AbstractHighlighterBuilder.java @@ -131,6 +131,7 @@ protected AbstractHighlighterBuilder(AbstractHighlighterBuilder template, Que /** * Read from a stream. */ + @SuppressWarnings("this-escape") protected AbstractHighlighterBuilder(StreamInput in) throws IOException { preTags(in.readOptionalStringArray()); postTags(in.readOptionalStringArray()); diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java index 721a84efcd751..cace74f4189fb 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java @@ -124,6 +124,7 @@ public HighlightBuilder(HighlightBuilder template, QueryBuilder highlightQuery, /** * Read from a stream. */ + @SuppressWarnings("this-escape") public HighlightBuilder(StreamInput in) throws IOException { super(in); encoder(in.readOptionalString()); @@ -474,6 +475,7 @@ private Field(Field template, QueryBuilder builder) { /** * Read from a stream. */ + @SuppressWarnings("this-escape") public Field(StreamInput in) throws IOException { super(in); name = in.readString(); diff --git a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java index 94bc5a4187435..4ccb409a1f6e0 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java @@ -96,6 +96,7 @@ public class ContextIndexSearcher extends IndexSearcher implements Releasable { private volatile boolean timeExceeded = false; /** constructor for non-concurrent search */ + @SuppressWarnings("this-escape") public ContextIndexSearcher( IndexReader reader, Similarity similarity, @@ -107,6 +108,7 @@ public ContextIndexSearcher( } /** constructor for concurrent search */ + @SuppressWarnings("this-escape") public ContextIndexSearcher( IndexReader reader, Similarity similarity, @@ -130,6 +132,7 @@ public ContextIndexSearcher( ); } + @SuppressWarnings("this-escape") ContextIndexSearcher( IndexReader reader, Similarity similarity, diff --git a/server/src/main/java/org/elasticsearch/search/internal/LegacyReaderContext.java b/server/src/main/java/org/elasticsearch/search/internal/LegacyReaderContext.java index d2e80e3ce1e0c..bbd626e05d1c8 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/LegacyReaderContext.java +++ b/server/src/main/java/org/elasticsearch/search/internal/LegacyReaderContext.java @@ -24,6 +24,7 @@ public class LegacyReaderContext extends ReaderContext { private AggregatedDfs aggregatedDfs; private RescoreDocIds rescoreDocIds; + @SuppressWarnings("this-escape") public LegacyReaderContext( ShardSearchContextId id, IndexService indexService, diff --git a/server/src/main/java/org/elasticsearch/search/internal/ReaderContext.java b/server/src/main/java/org/elasticsearch/search/internal/ReaderContext.java index d538280f10841..bbcd5482bdea0 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ReaderContext.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ReaderContext.java @@ -53,6 +53,7 @@ public class ReaderContext implements Releasable { private Map context; + @SuppressWarnings("this-escape") public ReaderContext( ShardSearchContextId id, IndexService indexService, diff --git a/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java b/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java index 842387c0ea13b..fbfcfdf9500ed 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java @@ -204,6 +204,7 @@ public ShardSearchRequest(ShardId shardId, long nowInMillis, AliasFilter aliasFi ); } + @SuppressWarnings("this-escape") public ShardSearchRequest( OriginalIndices originalIndices, ShardId shardId, @@ -246,6 +247,7 @@ public ShardSearchRequest( this.forceSyntheticSource = forceSyntheticSource; } + @SuppressWarnings("this-escape") public ShardSearchRequest(ShardSearchRequest clone) { this.shardId = clone.shardId; this.shardRequestIndex = clone.shardRequestIndex; diff --git a/server/src/main/java/org/elasticsearch/search/sort/BucketedSort.java b/server/src/main/java/org/elasticsearch/search/sort/BucketedSort.java index f3f15111f8e9b..ae2f7fc4ecbbb 100644 --- a/server/src/main/java/org/elasticsearch/search/sort/BucketedSort.java +++ b/server/src/main/java/org/elasticsearch/search/sort/BucketedSort.java @@ -455,6 +455,7 @@ private ExtraData.Loader loader() throws IOException { public abstract static class ForDoubles extends BucketedSort { private DoubleArray values; + @SuppressWarnings("this-escape") public ForDoubles(BigArrays bigArrays, SortOrder sortOrder, DocValueFormat format, int bucketSize, ExtraData extra) { super(bigArrays, sortOrder, format, bucketSize, extra); boolean success = false; @@ -555,6 +556,7 @@ public abstract static class ForFloats extends BucketedSort { private FloatArray values; + @SuppressWarnings("this-escape") public ForFloats(BigArrays bigArrays, SortOrder sortOrder, DocValueFormat format, int bucketSize, ExtraData extra) { super(bigArrays, sortOrder, format, bucketSize, extra); if (bucketSize > MAX_BUCKET_SIZE) { @@ -646,6 +648,7 @@ protected final boolean docBetterThan(long index) { public abstract static class ForLongs extends BucketedSort { private LongArray values; + @SuppressWarnings("this-escape") public ForLongs(BigArrays bigArrays, SortOrder sortOrder, DocValueFormat format, int bucketSize, ExtraData extra) { super(bigArrays, sortOrder, format, bucketSize, extra); boolean success = false; diff --git a/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java b/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java index 9a3999e4ce6e7..618de8c6f06f9 100644 --- a/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java @@ -101,6 +101,7 @@ public class FieldSortBuilder extends SortBuilder { private String format; /** Copy constructor. */ + @SuppressWarnings("this-escape") public FieldSortBuilder(FieldSortBuilder template) { this(template.fieldName); this.order(template.order()); diff --git a/server/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java b/server/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java index ea072c1bc43ca..3751186357ff6 100644 --- a/server/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/sort/ScoreSortBuilder.java @@ -44,6 +44,7 @@ public class ScoreSortBuilder extends SortBuilder { /** * Build a ScoreSortBuilder default to descending sort order. */ + @SuppressWarnings("this-escape") public ScoreSortBuilder() { // order defaults to desc when sorting on the _score order(SortOrder.DESC); @@ -52,6 +53,7 @@ public ScoreSortBuilder() { /** * Read from a stream. */ + @SuppressWarnings("this-escape") public ScoreSortBuilder(StreamInput in) throws IOException { order(SortOrder.readFromStream(in)); } diff --git a/server/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/server/src/main/java/org/elasticsearch/search/suggest/Suggest.java index cfa568d67cefc..657fc5a898b9d 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -61,6 +61,7 @@ public class Suggest implements Iterable>> suggestMap; + @SuppressWarnings("this-escape") public Suggest(List>> suggestions) { // we sort suggestions by their names to ensure iteration over suggestions are consistent // this is needed as we need to fill in suggestion docs in SearchPhaseController#sortDocs @@ -70,7 +71,7 @@ public Suggest(List>> suggestions) this.hasScoreDocs = filter(CompletionSuggestion.class).stream().anyMatch(CompletionSuggestion::hasScoreDocs); } - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({ "rawtypes", "unchecked", "this-escape" }) public Suggest(StreamInput in) throws IOException { suggestions = (List) in.readNamedWriteableCollectionAsList(Suggestion.class); hasScoreDocs = filter(CompletionSuggestion.class).stream().anyMatch(CompletionSuggestion::hasScoreDocs); @@ -216,6 +217,7 @@ public Suggestion(String name, int size) { this.size = size; // The suggested term size specified in request, only used for merging shard responses } + @SuppressWarnings("this-escape") public Suggestion(StreamInput in) throws IOException { name = in.readString(); size = in.readVInt(); @@ -405,6 +407,7 @@ public Entry(Text text, int offset, int length) { protected Entry() {} + @SuppressWarnings("this-escape") public Entry(StreamInput in) throws IOException { text = in.readText(); offset = in.readVInt(); diff --git a/server/src/main/java/org/elasticsearch/snapshots/InternalSnapshotsInfoService.java b/server/src/main/java/org/elasticsearch/snapshots/InternalSnapshotsInfoService.java index c8151e0212021..8451396e0e590 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/InternalSnapshotsInfoService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/InternalSnapshotsInfoService.java @@ -84,6 +84,7 @@ public class InternalSnapshotsInfoService implements ClusterStateListener, Snaps private final Object mutex; + @SuppressWarnings("this-escape") public InternalSnapshotsInfoService( final Settings settings, final ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java index 025a1840c04d9..d9fb26c4f4030 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java @@ -189,6 +189,7 @@ public class RestoreService implements ClusterStateApplier { private volatile boolean refreshRepositoryUuidOnRestore; + @SuppressWarnings("this-escape") public RestoreService( ClusterService clusterService, RepositoriesService repositoriesService, diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java index 145ce409dbe7c..bdd7fbd999a7b 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java @@ -88,6 +88,7 @@ public class SnapshotShardsService extends AbstractLifecycleComponent implements // Runs the tasks that promptly notify shards of aborted snapshots so that resources can be released ASAP private final ThrottledTaskRunner notifyOnAbortTaskRunner; + @SuppressWarnings("this-escape") public SnapshotShardsService( Settings settings, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 5fc4353a68230..7e75beae5bb81 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -197,6 +197,7 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus private volatile int maxConcurrentOperations; + @SuppressWarnings("this-escape") public SnapshotsService( Settings settings, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java index 811a4d8faaafb..9bf773d083f5f 100644 --- a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -136,6 +136,7 @@ public abstract class TcpTransport extends AbstractLifecycleComponent implements private final AtomicLong outboundConnectionCount = new AtomicLong(); // also used as a correlation ID for open/close logs + @SuppressWarnings("this-escape") public TcpTransport( Settings settings, TransportVersion version, diff --git a/server/src/main/java/org/elasticsearch/transport/TransportService.java b/server/src/main/java/org/elasticsearch/transport/TransportService.java index 7b1ca8d141c85..77b417e0bbc84 100644 --- a/server/src/main/java/org/elasticsearch/transport/TransportService.java +++ b/server/src/main/java/org/elasticsearch/transport/TransportService.java @@ -252,6 +252,7 @@ public TransportService( ); } + @SuppressWarnings("this-escape") public TransportService( Settings settings, Transport transport, diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java index 013f041dc2499..3de2b0a5d19a1 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java @@ -15,6 +15,7 @@ public class ObjectMapperMergeTests extends ESTestCase { + @SuppressWarnings("this-escape") private final RootObjectMapper rootObjectMapper = createMapping(false, true, true, false); private RootObjectMapper createMapping( diff --git a/server/src/test/java/org/elasticsearch/indices/analysis/lucene/SkipStartingWithDigitTokenFilter.java b/server/src/test/java/org/elasticsearch/indices/analysis/lucene/SkipStartingWithDigitTokenFilter.java index 3b53366f8f04d..5f6ebc09f4c0e 100644 --- a/server/src/test/java/org/elasticsearch/indices/analysis/lucene/SkipStartingWithDigitTokenFilter.java +++ b/server/src/test/java/org/elasticsearch/indices/analysis/lucene/SkipStartingWithDigitTokenFilter.java @@ -16,6 +16,7 @@ public class SkipStartingWithDigitTokenFilter extends FilteringTokenFilter { + @SuppressWarnings("this-escape") private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); private final long asciiDigitsToSkip; diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesActionTests.java index 5885c6f8c9885..81684d749d57c 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesActionTests.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; public class RestGetIndicesActionTests extends ESTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); /** diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java index 6a6ba43e3aeb8..e3a68b971ba42 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.mock; public class RestPutIndexTemplateActionTests extends ESTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); private RestPutIndexTemplateAction action; diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestDeleteActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestDeleteActionTests.java index 08cf03844d7d1..912cc13f49b3d 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestDeleteActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestDeleteActionTests.java @@ -22,6 +22,7 @@ public class RestDeleteActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetActionTests.java index ea5307746e50a..fc11557c2ec0d 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetActionTests.java @@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.instanceOf; public class RestGetActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java index 2e0c78d63ab9a..db859a4a15ff2 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestGetSourceActionTests.java @@ -42,6 +42,7 @@ public class RestGetSourceActionTests extends RestActionTestCase { private static RestRequest request = new FakeRestRequest(); private static FakeRestChannel channel = new FakeRestChannel(request, true, 0); private static RestGetSourceResponseListener listener = new RestGetSourceResponseListener(channel, request); + @SuppressWarnings("this-escape") private final List compatibleMediaType = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java index 237f38cf145ae..3b3a94a54595a 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java @@ -39,6 +39,7 @@ public class RestIndexActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); private final AtomicReference clusterStateSupplier = new AtomicReference<>(); diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiGetActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiGetActionTests.java index 2b2e94be3e6f3..9e1d7e7a5306e 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiGetActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiGetActionTests.java @@ -29,7 +29,9 @@ import static org.hamcrest.Matchers.instanceOf; public class RestMultiGetActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") XContentType VND_TYPE = randomVendorType(); + @SuppressWarnings("this-escape") List contentTypeHeader = Collections.singletonList(compatibleMediaType(VND_TYPE, RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiTermVectorsActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiTermVectorsActionTests.java index dab1bade79218..323861171723a 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiTermVectorsActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestMultiTermVectorsActionTests.java @@ -27,6 +27,7 @@ import java.util.Map; public class RestMultiTermVectorsActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestTermVectorsActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestTermVectorsActionTests.java index 23b9772be8d88..7702bda695616 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestTermVectorsActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestTermVectorsActionTests.java @@ -26,6 +26,7 @@ import java.util.Map; public class RestTermVectorsActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/document/RestUpdateActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/document/RestUpdateActionTests.java index 2453467621ed0..e7c00ab8bb0de 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/document/RestUpdateActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/document/RestUpdateActionTests.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.mock; public class RestUpdateActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); private RestUpdateAction action; diff --git a/server/src/test/java/org/elasticsearch/rest/action/search/RestCountActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/search/RestCountActionTests.java index f8868baa18858..d8a7a4a1dfe52 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/search/RestCountActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/search/RestCountActionTests.java @@ -27,6 +27,7 @@ public class RestCountActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java index 5d0f815173595..d9ae400a860ff 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java @@ -22,6 +22,7 @@ import java.util.Map; public class RestExplainActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); @Before diff --git a/server/src/test/java/org/elasticsearch/rest/action/search/RestMultiSearchActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/search/RestMultiSearchActionTests.java index a0af62d2d9299..74bd423cce967 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/search/RestMultiSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/search/RestMultiSearchActionTests.java @@ -26,6 +26,7 @@ import java.util.Map; public class RestMultiSearchActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(compatibleMediaType(XContentType.VND_JSON, RestApiVersion.V_7)); private RestMultiSearchAction action; diff --git a/server/src/test/java/org/elasticsearch/rest/action/search/RestSearchActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/search/RestSearchActionTests.java index 75fabe0d47e20..3c9f5422c30fa 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/search/RestSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/search/RestSearchActionTests.java @@ -29,6 +29,7 @@ import java.util.Map; public class RestSearchActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") final List contentTypeHeader = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); private RestSearchAction action; diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java index 08ae0b028e5c7..f93cc91602b34 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java @@ -292,10 +292,12 @@ public class Cluster implements Releasable { this(initialNodeCount, true, Settings.EMPTY); } + @SuppressWarnings("this-escape") public Cluster(int initialNodeCount, boolean allNodesMasterEligible, Settings nodeSettings) { this(initialNodeCount, allNodesMasterEligible, nodeSettings, () -> new StatusInfo(HEALTHY, "healthy-info")); } + @SuppressWarnings("this-escape") Cluster(int initialNodeCount, boolean allNodesMasterEligible, Settings nodeSettings, NodeHealthService nodeHealthService) { this.nodeHealthService = nodeHealthService; this.countingPageCacheRecycler = new CountingPageCacheRecycler(); @@ -975,6 +977,7 @@ public class ClusterNode { private ClearableRecycler clearableRecycler; private List blackholedRegisterOperations = new ArrayList<>(); + @SuppressWarnings("this-escape") ClusterNode(int nodeIndex, boolean masterEligible, Settings nodeSettings, NodeHealthService nodeHealthService) { this( nodeIndex, diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/CoordinationStateTestCluster.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/CoordinationStateTestCluster.java index 76d662e594644..6e71b95a7ecab 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/CoordinationStateTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/CoordinationStateTestCluster.java @@ -183,6 +183,7 @@ void setInitialState(CoordinationMetadata.VotingConfiguration initialConfig, lon final CoordinationMetadata.VotingConfiguration initialConfiguration; final long initialValue; + @SuppressWarnings("this-escape") public CoordinationStateTestCluster(List nodes, ElectionStrategy electionStrategy) { this.electionStrategy = electionStrategy; messages = new ArrayList<>(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractBootstrapCheckTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractBootstrapCheckTestCase.java index 3afb8bf026298..07db3a182450d 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractBootstrapCheckTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractBootstrapCheckTestCase.java @@ -19,6 +19,7 @@ public abstract class AbstractBootstrapCheckTestCase extends ESTestCase { protected final BootstrapContext emptyContext; + @SuppressWarnings("this-escape") public AbstractBootstrapCheckTestCase() { emptyContext = createTestContext(Settings.EMPTY, Metadata.EMPTY_METADATA); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/BackgroundIndexer.java b/test/framework/src/main/java/org/elasticsearch/test/BackgroundIndexer.java index eb0102e118b9d..9829e40088829 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/BackgroundIndexer.java +++ b/test/framework/src/main/java/org/elasticsearch/test/BackgroundIndexer.java @@ -98,6 +98,7 @@ public BackgroundIndexer(String index, Client client, int numOfDocs, final int w * @param autoStart set to true to start indexing as soon as all threads have been created. * @param random random instance to use */ + @SuppressWarnings("this-escape") public BackgroundIndexer( final String index, final Client client, diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 008f8511ee127..530eafb84d47b 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -238,8 +238,10 @@ public AggregationReduceContext forFinalReduction() { } }; + @SuppressWarnings("this-escape") private final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(getNamedWriteables()); + @SuppressWarnings("this-escape") private final NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(getNamedXContents()); private static final List namedXContents; diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java index 5f8c6ace00246..c20e1ce70e601 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java @@ -78,6 +78,7 @@ public static class VerifyingClient extends NoOpNodeClient { AtomicReference, ActionRequest, ActionResponse>> executeVerifier = new AtomicReference<>(); AtomicReference, ActionRequest, ActionResponse>> executeLocallyVerifier = new AtomicReference<>(); + @SuppressWarnings("this-escape") public VerifyingClient(String testName) { super(testName); reset(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransport.java b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransport.java index 003912b32c5a6..194d7ddb798ca 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransport.java +++ b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransport.java @@ -78,6 +78,7 @@ public TransportService createTransportService( ); } + @SuppressWarnings("this-escape") public MockTransport() { super(new FakeTransport()); setDefaultConnectBehavior( diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java index dd47fd057ac1c..c9f89453858ce 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterSpecBuilder.java @@ -16,6 +16,7 @@ public class DefaultLocalClusterSpecBuilder extends AbstractLocalClusterSpecBuilder { + @SuppressWarnings("this-escape") public DefaultLocalClusterSpecBuilder() { super(); this.apply(new FipsEnabledClusterConfigProvider()); diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java index 81e34b3840e3e..2e76da1a519bd 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java @@ -29,6 +29,7 @@ public class HistoBackedHistogramAggregator extends AbstractHistogramAggregator private final HistogramValuesSource.Histogram valuesSource; + @SuppressWarnings("this-escape") public HistoBackedHistogramAggregator( String name, AggregatorFactories factories, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java index 1eaee57ee663c..b2be65a9e2901 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java @@ -79,6 +79,7 @@ public static HistoBackedRangeAggregator build( ); } + @SuppressWarnings("this-escape") public HistoBackedRangeAggregator( String name, AggregatorFactories factories, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedAvgAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedAvgAggregator.java index 6e9edb88603e7..2b80084e2d92c 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedAvgAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedAvgAggregator.java @@ -41,6 +41,7 @@ public class HistoBackedAvgAggregator extends NumericMetricsAggregator.SingleVal DoubleArray compensations; DocValueFormat format; + @SuppressWarnings("this-escape") public HistoBackedAvgAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMaxAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMaxAggregator.java index 675f8856308e1..f094c22e4dff8 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMaxAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMaxAggregator.java @@ -32,6 +32,7 @@ public class HistoBackedMaxAggregator extends NumericMetricsAggregator.SingleVal final DocValueFormat formatter; DoubleArray maxes; + @SuppressWarnings("this-escape") public HistoBackedMaxAggregator( String name, ValuesSourceConfig config, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMinAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMinAggregator.java index d0e4a1712a8d3..ecf89f8eab15f 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMinAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedMinAggregator.java @@ -32,6 +32,7 @@ public class HistoBackedMinAggregator extends NumericMetricsAggregator.SingleVal final DocValueFormat format; DoubleArray mins; + @SuppressWarnings("this-escape") public HistoBackedMinAggregator( String name, ValuesSourceConfig config, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedSumAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedSumAggregator.java index 18ac10d5e6cb6..ebf1a43e38e2a 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedSumAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedSumAggregator.java @@ -41,6 +41,7 @@ public class HistoBackedSumAggregator extends NumericMetricsAggregator.SingleVal private DoubleArray sums; private DoubleArray compensations; + @SuppressWarnings("this-escape") public HistoBackedSumAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedValueCountAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedValueCountAggregator.java index 841df0f06fab2..184fd7072fa65 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedValueCountAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/metrics/HistoBackedValueCountAggregator.java @@ -36,6 +36,7 @@ public class HistoBackedValueCountAggregator extends NumericMetricsAggregator.Si /** Count per bucket */ LongArray counts; + @SuppressWarnings("this-escape") public HistoBackedValueCountAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/AbstractRateAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/AbstractRateAggregator.java index 263969b59e932..6e4078478c678 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/AbstractRateAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/AbstractRateAggregator.java @@ -34,6 +34,7 @@ public abstract class AbstractRateAggregator extends NumericMetricsAggregator.Si protected DoubleArray sums; protected DoubleArray compensations; + @SuppressWarnings("this-escape") public AbstractRateAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/TimeSeriesRateAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/TimeSeriesRateAggregator.java index 486279e646b64..da8dfb3d480e0 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/TimeSeriesRateAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/TimeSeriesRateAggregator.java @@ -48,6 +48,7 @@ public class TimeSeriesRateAggregator extends NumericMetricsAggregator.SingleVal private final Rounding.DateTimeUnit rateUnit; // Unused parameters are so that the constructor implements `RateAggregatorSupplier` + @SuppressWarnings("this-escape") protected TimeSeriesRateAggregator( String name, ValuesSourceConfig valuesSourceConfig, diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java index e2fc1707c0499..c832a3c7eb461 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java @@ -97,10 +97,12 @@ public class Autoscaling extends Plugin implements ActionPlugin, ExtensiblePlugi private final AutoscalingLicenseChecker autoscalingLicenseChecker; private final SetOnce reservedAutoscalingPolicyAction = new SetOnce<>(); + @SuppressWarnings("this-escape") public Autoscaling() { this(new AutoscalingLicenseChecker()); } + @SuppressWarnings("this-escape") Autoscaling(final AutoscalingLicenseChecker autoscalingLicenseChecker) { this.autoscalingExtensions = new ArrayList<>(List.of(this)); this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java index c7d59c8a48d26..30c20f419ebba 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java @@ -45,6 +45,7 @@ public class ShardFollowTaskCleaner implements ClusterStateListener { */ private final Set completing = Collections.synchronizedSet(new HashSet<>()); + @SuppressWarnings("this-escape") public ShardFollowTaskCleaner(final ClusterService clusterService, final ThreadPool threadPool, final Client client) { this.threadPool = threadPool; this.client = client; diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java index 1ee15732d1339..f53e7bb562122 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java @@ -100,6 +100,7 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor(in, getReader()); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackInfoAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackInfoAction.java index aac0230c1028a..f2443d3b6e0f8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackInfoAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackInfoAction.java @@ -32,6 +32,7 @@ public class TransportXPackInfoAction extends HandledTransportAction infoActions; + @SuppressWarnings("this-escape") @Inject public TransportXPackInfoAction( TransportService transportService, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java index 6a9d00e62e975..1fb2664dac007 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java @@ -30,6 +30,7 @@ public class TransportXPackUsageAction extends TransportMasterNodeAction usageActions; + @SuppressWarnings("this-escape") @Inject public TransportXPackUsageAction( ThreadPool threadPool, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/StoredAsyncTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/StoredAsyncTask.java index f16d9cd2260e4..9f75ac0f5f564 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/StoredAsyncTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/StoredAsyncTask.java @@ -25,6 +25,7 @@ public abstract class StoredAsyncTask extends C private volatile long expirationTimeMillis; private final List> completionListeners; + @SuppressWarnings("this-escape") public StoredAsyncTask( long id, String type, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/FollowParameters.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/FollowParameters.java index c07143306ae05..b0493dbc30bfe 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/FollowParameters.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/FollowParameters.java @@ -189,6 +189,7 @@ public ActionRequestValidationException validate() { return e; } + @SuppressWarnings("this-escape") public FollowParameters(StreamInput in) throws IOException { fromStreamInput(in); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutFollowAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutFollowAction.java index 0d8c120303cb5..0bc3e83df77ea 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutFollowAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutFollowAction.java @@ -188,6 +188,7 @@ public IndicesOptions indicesOptions() { return IndicesOptions.strictSingleIndexNoExpandForbidClosed(); } + @SuppressWarnings("this-escape") public Request(StreamInput in) throws IOException { super(in); this.remoteCluster = in.readString(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexing/AsyncTwoPhaseIndexer.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexing/AsyncTwoPhaseIndexer.java index 54b9fe7d76a85..e811c38740618 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexing/AsyncTwoPhaseIndexer.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexing/AsyncTwoPhaseIndexer.java @@ -45,6 +45,7 @@ public abstract class AsyncTwoPhaseIndexer searchResponseListener = ActionListener.wrap( this::onSearchResponse, this::finishWithSearchFailure diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/DeleteDataFrameAnalyticsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/DeleteDataFrameAnalyticsAction.java index e06427681c2ee..e96836ab6a5fa 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/DeleteDataFrameAnalyticsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/DeleteDataFrameAnalyticsAction.java @@ -48,6 +48,7 @@ public Request(StreamInput in) throws IOException { force = in.readBoolean(); } + @SuppressWarnings("this-escape") public Request() { timeout(DEFAULT_TIMEOUT); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDataFrameAnalyticsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDataFrameAnalyticsAction.java index df832c38aff13..599d992819427 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDataFrameAnalyticsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDataFrameAnalyticsAction.java @@ -31,10 +31,12 @@ public static class Request extends AbstractGetResourcesRequest { public static final ParseField ALLOW_NO_MATCH = new ParseField("allow_no_match"); + @SuppressWarnings("this-escape") public Request() { setAllowNoResources(true); } + @SuppressWarnings("this-escape") public Request(String id) { setResourceId(id); setAllowNoResources(true); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDatafeedsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDatafeedsAction.java index 88de64553c5f5..9637ba3c0f92e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDatafeedsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetDatafeedsAction.java @@ -49,6 +49,7 @@ public Request(String datafeedId) { this.datafeedId = ExceptionsHelper.requireNonNull(datafeedId, DatafeedConfig.ID.getPreferredName()); } + @SuppressWarnings("this-escape") public Request() { local(true); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetFiltersAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetFiltersAction.java index 32e913cbf1eb7..e213703b56892 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetFiltersAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetFiltersAction.java @@ -29,10 +29,12 @@ private GetFiltersAction() { public static class Request extends AbstractGetResourcesRequest { + @SuppressWarnings("this-escape") public Request() { setAllowNoResources(true); } + @SuppressWarnings("this-escape") public Request(String filterId) { setResourceId(filterId); setAllowNoResources(true); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetJobsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetJobsAction.java index fd1df69dad73d..41358dc34f40b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetJobsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetJobsAction.java @@ -47,6 +47,7 @@ public Request(String jobId) { this.jobId = ExceptionsHelper.requireNonNull(jobId, Job.ID.getPreferredName()); } + @SuppressWarnings("this-escape") public Request() { local(true); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsAction.java index 0e58a5946a277..3d152048563c4 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsAction.java @@ -136,6 +136,7 @@ public Request(String id) { this(id, null, null); } + @SuppressWarnings("this-escape") public Request(String id, List tags, Set includes) { setResourceId(id); setAllowNoResources(true); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsStatsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsStatsAction.java index 11f2a4191afe8..cd1b5674dcb7b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsStatsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/GetTrainedModelsStatsAction.java @@ -56,10 +56,12 @@ public static class Request extends AbstractGetResourcesRequest { public static final ParseField ALLOW_NO_MATCH = new ParseField("allow_no_match"); + @SuppressWarnings("this-escape") public Request() { setAllowNoResources(true); } + @SuppressWarnings("this-escape") public Request(String id) { setResourceId(id); setAllowNoResources(true); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StopDataFrameAnalyticsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StopDataFrameAnalyticsAction.java index c5ad45d1f6ce9..48a793155e542 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StopDataFrameAnalyticsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StopDataFrameAnalyticsAction.java @@ -90,6 +90,7 @@ public Request(StreamInput in) throws IOException { expandedIds = new HashSet<>(Arrays.asList(in.readStringArray())); } + @SuppressWarnings("this-escape") public Request() { setTimeout(DEFAULT_TIMEOUT); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/classification/Classification.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/classification/Classification.java index 373402be8419d..956c4713adb35 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/classification/Classification.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/classification/Classification.java @@ -75,6 +75,7 @@ public static Classification fromXContent(XContentParser parser) { */ private final List metrics; + @SuppressWarnings("this-escape") public Classification( String actualField, @Nullable String predictedField, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/outlierdetection/OutlierDetection.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/outlierdetection/OutlierDetection.java index 5e924558d5d77..84b82bde909a0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/outlierdetection/OutlierDetection.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/outlierdetection/OutlierDetection.java @@ -75,6 +75,7 @@ public static QueryBuilder actualIsTrueQuery(String actualField) { */ private final List metrics; + @SuppressWarnings("this-escape") public OutlierDetection(String actualField, String predictedProbabilityField, @Nullable List metrics) { this.fields = new EvaluationFields( ExceptionsHelper.requireNonNull(actualField, ACTUAL_FIELD), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/regression/Regression.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/regression/Regression.java index f4b8866cae9bd..4b1e444f32224 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/regression/Regression.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/evaluation/regression/Regression.java @@ -69,6 +69,7 @@ public static Regression fromXContent(XContentParser parser) { */ private final List metrics; + @SuppressWarnings("this-escape") public Regression(String actualField, String predictedField, @Nullable List metrics) { this.fields = new EvaluationFields( ExceptionsHelper.requireNonNull(actualField, ACTUAL_FIELD), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/assignment/TrainedModelAssignment.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/assignment/TrainedModelAssignment.java index cd69d67b0425c..3664e4f620266 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/assignment/TrainedModelAssignment.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/assignment/TrainedModelAssignment.java @@ -137,6 +137,7 @@ private TrainedModelAssignment( : Math.max(maxAssignedAllocations, totalCurrentAllocations()); } + @SuppressWarnings("this-escape") public TrainedModelAssignment(StreamInput in) throws IOException { this.taskParams = new StartTrainedModelDeploymentAction.TaskParams(in); this.nodeRoutingTable = in.readOrderedMap(StreamInput::readString, RoutingInfo::new); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/tree/Tree.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/tree/Tree.java index 458cbb2b602f5..b472c6ef32163 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/tree/Tree.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/tree/Tree.java @@ -302,6 +302,7 @@ public static class Builder { private TargetType targetType = TargetType.REGRESSION; private List classificationLabels; + @SuppressWarnings("this-escape") public Builder() { nodes = new ArrayList<>(); // allocate space in the root node and set to a leaf diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisConfig.java index ea893a69fa392..21b572907d037 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisConfig.java @@ -464,6 +464,7 @@ public static class Builder { private Boolean multivariateByFields; private TimeValue modelPruneWindow; + @SuppressWarnings("this-escape") public Builder(List detectors) { setDetectors(detectors); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/DataCounts.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/DataCounts.java index c817ff9d99a3b..827b25f39f23f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/DataCounts.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/DataCounts.java @@ -161,6 +161,7 @@ public static String v54DocumentId(String jobId) { private Date latestSparseBucketTimeStamp; private Instant logTime; + @SuppressWarnings("this-escape") public DataCounts( String jobId, long processedRecordCount, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/ForecastRequestStats.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/ForecastRequestStats.java index 620618becbb3e..3cccb0006d658 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/ForecastRequestStats.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/ForecastRequestStats.java @@ -147,6 +147,7 @@ public ForecastRequestStats(ForecastRequestStats forecastRequestStats) { this.status = forecastRequestStats.status; } + @SuppressWarnings("this-escape") public ForecastRequestStats(StreamInput in) throws IOException { jobId = in.readString(); forecastId = in.readString(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/AbstractCreateApiKeyRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/AbstractCreateApiKeyRequest.java index d6c97aff52b35..998d35267be37 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/AbstractCreateApiKeyRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/AbstractCreateApiKeyRequest.java @@ -39,6 +39,7 @@ public AbstractCreateApiKeyRequest() { this.id = UUIDs.base64UUID(); // because auditing can currently only catch requests but not responses, } + @SuppressWarnings("this-escape") public AbstractCreateApiKeyRequest(StreamInput in) throws IOException { super(in); this.id = doReadId(in); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmConfig.java index fc3274e796f9a..c2c64b45941cd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmConfig.java @@ -34,6 +34,7 @@ public class RealmConfig { private final Settings settings; private final ThreadContext threadContext; + @SuppressWarnings("this-escape") public RealmConfig(RealmIdentifier identifier, Settings settings, Environment env, ThreadContext threadContext) { this.identifier = identifier; this.settings = settings; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ActionClusterPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ActionClusterPrivilege.java index ecd41e02589f4..20ff6f4c4ad17 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ActionClusterPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ActionClusterPrivilege.java @@ -39,6 +39,7 @@ public ActionClusterPrivilege(final String name, final Set allowedAction * @param allowedActionPatterns a set of cluster action patterns * @param excludedActionPatterns a set of cluster action patterns */ + @SuppressWarnings("this-escape") public ActionClusterPrivilege(final String name, final Set allowedActionPatterns, final Set excludedActionPatterns) { this.name = name; this.allowedActionPatterns = allowedActionPatterns; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java index 674318e9b20b8..9704335776f11 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java @@ -151,6 +151,7 @@ public SSLService(Environment environment) { * Create a new SSLService using the provided {@link SslConfiguration} instances. The ssl * contexts created from these configurations will be cached. */ + @SuppressWarnings("this-escape") public SSLService(Environment environment, Map sslConfigurations) { this.env = environment; this.settings = env.settings(); @@ -159,6 +160,7 @@ public SSLService(Environment environment, Map sslConf this.sslContexts = loadSslConfigurations(this.sslConfigurations); } + @SuppressWarnings("this-escape") @Deprecated public SSLService(Settings settings, Environment environment) { this.env = environment; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java index 2196558cf990b..438c457d84305 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java @@ -40,6 +40,7 @@ public class SslSettingsLoader extends SslConfigurationLoader { private final Map> standardSettings; private final Map> disabledSettings; + @SuppressWarnings("this-escape") public SslSettingsLoader(Settings settings, String settingPrefix, boolean acceptNonSecurePasswords) { super(settingPrefix); this.settings = settings; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/IndexTemplateRegistry.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/IndexTemplateRegistry.java index 7a91c7f0bd5d0..ae47fff47f82e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/IndexTemplateRegistry.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/IndexTemplateRegistry.java @@ -86,6 +86,7 @@ public static boolean isDataStreamsLifecycleOnlyMode(final Settings settings) { protected final ConcurrentMap pipelineCreationsInProgress = new ConcurrentHashMap<>(); protected final List lifecyclePolicies; + @SuppressWarnings("this-escape") public IndexTemplateRegistry( Settings nodeSettings, ClusterService clusterService, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TermsEnumRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TermsEnumRequest.java index a6982607c1b0a..46850e5f4d067 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TermsEnumRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TermsEnumRequest.java @@ -51,12 +51,14 @@ public TermsEnumRequest() { * Constructs a new term enum request against the provided indices. No indices provided means it will * run against all indices. */ + @SuppressWarnings("this-escape") public TermsEnumRequest(String... indices) { super(indices); indicesOptions(DEFAULT_INDICES_OPTIONS); timeout(DEFAULT_TIMEOUT); } + @SuppressWarnings("this-escape") public TermsEnumRequest(TermsEnumRequest clone) { this.field = clone.field; this.string = clone.string; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/textstructure/structurefinder/TextStructure.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/textstructure/structurefinder/TextStructure.java index c37c795f5a94c..1a59f373d75f3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/textstructure/structurefinder/TextStructure.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/textstructure/structurefinder/TextStructure.java @@ -553,6 +553,7 @@ public Builder() { this(Format.SEMI_STRUCTURED_TEXT); } + @SuppressWarnings("this-escape") public Builder(Format format) { setFormat(format); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetTransformStatsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetTransformStatsAction.java index 31a577dd43a43..5ff153db7467d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetTransformStatsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetTransformStatsAction.java @@ -58,6 +58,7 @@ public static class Request extends BaseTasksRequest { // used internally to expand the queried id expression private List expandedIds; + @SuppressWarnings("this-escape") public Request(String id, @Nullable TimeValue timeout) { setTimeout(timeout); if (Strings.isNullOrEmpty(id) || id.equals("*")) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/ScheduleNowTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/ScheduleNowTransformAction.java index 96d3f498e8ded..57c14d17cce40 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/ScheduleNowTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/ScheduleNowTransformAction.java @@ -41,6 +41,7 @@ public static class Request extends BaseTasksRequest { private final String id; + @SuppressWarnings("this-escape") public Request(String id, TimeValue timeout) { this.id = ExceptionsHelper.requireNonNull(id, TransformField.ID.getPreferredName()); this.setTimeout(ExceptionsHelper.requireNonNull(timeout, TransformField.TIMEOUT.getPreferredName())); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/StopTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/StopTransformAction.java index b9852e1f2a0d0..097ae6bb05a07 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/StopTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/StopTransformAction.java @@ -54,6 +54,7 @@ public static class Request extends BaseTasksRequest { private final boolean waitForCheckpoint; private Set expandedIds; + @SuppressWarnings("this-escape") public Request( String id, boolean waitForCompletion, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/UpdateTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/UpdateTransformAction.java index 6ecdd6519714b..e943d0dd50ac8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/UpdateTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/UpdateTransformAction.java @@ -50,6 +50,7 @@ public static class Request extends BaseTasksRequest { private TransformConfig config; private AuthorizationState authState; + @SuppressWarnings("this-escape") public Request(TransformConfigUpdate update, String id, boolean deferValidation, TimeValue timeout) { this.update = update; this.id = id; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java index 767b83f264184..63197f7903004 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java @@ -207,6 +207,7 @@ public static String documentId(String transformId) { return NAME + "-" + transformId; } + @SuppressWarnings("this-escape") public TransformConfig( final String id, final SourceConfig source, @@ -242,6 +243,7 @@ public TransformConfig( this.transformVersion = version == null ? null : TransformConfigVersion.fromString(version); } + @SuppressWarnings("this-escape") public TransformConfig(final StreamInput in) throws IOException { id = in.readString(); source = new SourceConfig(in); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigUpdate.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigUpdate.java index c3f4d3d382547..beecea3b9f054 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigUpdate.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigUpdate.java @@ -107,6 +107,7 @@ public TransformConfigUpdate( this.retentionPolicyConfig = retentionPolicyConfig; } + @SuppressWarnings("this-escape") public TransformConfigUpdate(final StreamInput in) throws IOException { source = in.readOptionalWriteable(SourceConfig::new); dest = in.readOptionalWriteable(DestConfig::new); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/common/stats/Counters.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/common/stats/Counters.java index 098ca5cd76200..fe649a203bcce 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/common/stats/Counters.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/common/stats/Counters.java @@ -25,6 +25,7 @@ public class Counters implements Writeable { private Map counters = new HashMap<>(); + @SuppressWarnings("this-escape") public Counters(StreamInput in) throws IOException { int numCounters = in.readVInt(); for (int i = 0; i < numCounters; i++) { @@ -32,6 +33,7 @@ public Counters(StreamInput in) throws IOException { } } + @SuppressWarnings("this-escape") public Counters(String... names) { for (String name : names) { set(name); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/TransformRegistry.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/TransformRegistry.java index ab7a4bf5964fc..8d1f521fcd7e2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/TransformRegistry.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/TransformRegistry.java @@ -22,6 +22,7 @@ public class TransformRegistry { String, TransformFactory>> factories; + @SuppressWarnings("this-escape") public TransformRegistry( Map>> factories ) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/chain/ChainTransform.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/chain/ChainTransform.java index 1ac2c7f57845d..6d587468ff0dd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/chain/ChainTransform.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transform/chain/ChainTransform.java @@ -147,6 +147,7 @@ public static class Builder implements Transform.Builder { private final List transforms = new ArrayList<>(); + @SuppressWarnings("this-escape") public Builder(Transform... transforms) { add(transforms); } diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/AggregateMetricFieldValueFetcher.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/AggregateMetricFieldValueFetcher.java index ec98cbeb152f4..dbdc09712eb25 100644 --- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/AggregateMetricFieldValueFetcher.java +++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/AggregateMetricFieldValueFetcher.java @@ -19,6 +19,7 @@ public class AggregateMetricFieldValueFetcher extends FieldValueFetcher { private final AbstractDownsampleFieldProducer fieldProducer; + @SuppressWarnings("this-escape") protected AggregateMetricFieldValueFetcher( MappedFieldType fieldType, AggregateDoubleMetricFieldType aggMetricFieldType, diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java index d43f69f1ee662..3ac70ccc4b9c1 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java @@ -42,6 +42,7 @@ public class EqlFunctionRegistry extends FunctionRegistry { + @SuppressWarnings("this-escape") public EqlFunctionRegistry() { register(functions()); } diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/math/ToNumber.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/math/ToNumber.java index 2a501c5ae6e0f..6bd7d76737849 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/math/ToNumber.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/math/ToNumber.java @@ -40,6 +40,7 @@ public class ToNumber extends ScalarFunction implements OptionalArgument { private final Expression value, base; + @SuppressWarnings("this-escape") public ToNumber(Source source, Expression value, Expression base) { super(source, Arrays.asList(value, base != null ? base : new Literal(source, null, DataTypes.NULL))); this.value = value; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Between.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Between.java index b8bc877b614c3..c0eb7e42e7e56 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Between.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Between.java @@ -44,6 +44,7 @@ public class Between extends CaseInsensitiveScalarFunction implements OptionalAr private final Expression input, left, right, greedy; + @SuppressWarnings("this-escape") public Between(Source source, Expression input, Expression left, Expression right, Expression greedy, boolean caseInsensitive) { super(source, Arrays.asList(input, left, right, defaultGreedy(greedy)), caseInsensitive); this.input = input; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/IndexOf.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/IndexOf.java index e7ead20b407cb..fe8d60cc46986 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/IndexOf.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/IndexOf.java @@ -40,6 +40,7 @@ public class IndexOf extends CaseInsensitiveScalarFunction implements OptionalAr private final Expression input, substring, start; + @SuppressWarnings("this-escape") public IndexOf(Source source, Expression input, Expression substring, Expression start, boolean caseInsensitive) { super(source, asList(input, substring, start != null ? start : new Literal(source, null, DataTypes.NULL)), caseInsensitive); this.input = input; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Match.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Match.java index 9a6f7642b53b1..4137d8d41e3fe 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Match.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Match.java @@ -38,10 +38,12 @@ public class Match extends BaseSurrogateFunction { private final List patterns; private final boolean caseInsensitive; + @SuppressWarnings("this-escape") public Match(Source source, Expression field, List patterns, boolean caseInsensitive) { this(source, CollectionUtils.combine(singletonList(field), patterns), caseInsensitive); } + @SuppressWarnings("this-escape") private Match(Source source, List children, boolean caseInsensitive) { super(source, children); this.field = children().get(0); diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java index 4fe946b5cd1c5..524ccf0422ccb 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java @@ -42,6 +42,7 @@ public class Substring extends ScalarFunction implements OptionalArgument { private final Expression input, start, end; + @SuppressWarnings("this-escape") public Substring(Source source, Expression input, Expression start, Expression end) { super(source, Arrays.asList(input, start, end != null ? end : new Literal(source, null, DataTypes.NULL))); this.input = input; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java index d8a2e51719499..77d085b5fba1a 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/TransportEqlSearchAction.java @@ -73,6 +73,7 @@ public class TransportEqlSearchAction extends HandledTransportAction asyncTaskManagementService; + @SuppressWarnings("this-escape") @Inject public TransportEqlSearchAction( Settings settings, diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/HashAggregationOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/HashAggregationOperator.java index 585ab18c75e2a..33843286e416b 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/HashAggregationOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/HashAggregationOperator.java @@ -59,6 +59,7 @@ public String describe() { private final List aggregators; + @SuppressWarnings("this-escape") public HashAggregationOperator( List aggregators, Supplier blockHash, diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java index eecb2821ca135..3a675f1024387 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java @@ -87,6 +87,7 @@ public class EsqlFunctionRegistry extends FunctionRegistry { + @SuppressWarnings("this-escape") public EsqlFunctionRegistry() { register(functions()); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/UnsupportedAttribute.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/UnsupportedAttribute.java index b8353b8789786..6dcba915186a4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/UnsupportedAttribute.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/UnsupportedAttribute.java @@ -42,6 +42,7 @@ public UnsupportedAttribute(Source source, String name, UnsupportedEsField field this(source, name, field, customMessage, null); } + @SuppressWarnings("this-escape") public UnsupportedAttribute(Source source, String name, UnsupportedEsField field, String customMessage, NameId id) { super(source, null, name, field, null, Nullability.TRUE, id, false); this.hasCustomMessage = customMessage != null; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java index 909b27e20530d..4abb1fa5349fe 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java @@ -44,6 +44,7 @@ record Condition(Expression condition, Expression value) {} private final Expression elseValue; private DataType dataType; + @SuppressWarnings("this-escape") public Case(Source source, Expression first, List rest) { super(source, Stream.concat(Stream.of(first), rest.stream()).toList()); int conditionCount = children().size() / 2; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java index be46b6c6e1797..d7b31cd220760 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java @@ -127,7 +127,7 @@ public Vocabulary getVocabulary() { } - public EsqlBaseLexer(CharStream input) { + @SuppressWarnings("this-escape") public EsqlBaseLexer(CharStream input) { super(input); _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java index b5eac5f58f9f6..79cca599aabac 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java @@ -138,7 +138,7 @@ public Vocabulary getVocabulary() { @Override public ATN getATN() { return _ATN; } - public EsqlBaseParser(TokenStream input) { + @SuppressWarnings("this-escape") public EsqlBaseParser(TokenStream input) { super(input); _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); } @@ -212,7 +212,7 @@ public QueryContext query() { public ProcessingCommandContext processingCommand() { return getRuleContext(ProcessingCommandContext.class,0); } - public CompositeQueryContext(QueryContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public CompositeQueryContext(QueryContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterCompositeQuery(this); @@ -232,7 +232,7 @@ public static class SingleCommandQueryContext extends QueryContext { public SourceCommandContext sourceCommand() { return getRuleContext(SourceCommandContext.class,0); } - public SingleCommandQueryContext(QueryContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public SingleCommandQueryContext(QueryContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterSingleCommandQuery(this); @@ -632,7 +632,7 @@ public static class LogicalNotContext extends BooleanExpressionContext { public BooleanExpressionContext booleanExpression() { return getRuleContext(BooleanExpressionContext.class,0); } - public LogicalNotContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public LogicalNotContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterLogicalNot(this); @@ -652,7 +652,7 @@ public static class BooleanDefaultContext extends BooleanExpressionContext { public ValueExpressionContext valueExpression() { return getRuleContext(ValueExpressionContext.class,0); } - public BooleanDefaultContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public BooleanDefaultContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterBooleanDefault(this); @@ -675,7 +675,7 @@ public ValueExpressionContext valueExpression() { public TerminalNode IS() { return getToken(EsqlBaseParser.IS, 0); } public TerminalNode NULL() { return getToken(EsqlBaseParser.NULL, 0); } public TerminalNode NOT() { return getToken(EsqlBaseParser.NOT, 0); } - public IsNullContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public IsNullContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterIsNull(this); @@ -695,7 +695,7 @@ public static class RegexExpressionContext extends BooleanExpressionContext { public RegexBooleanExpressionContext regexBooleanExpression() { return getRuleContext(RegexBooleanExpressionContext.class,0); } - public RegexExpressionContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public RegexExpressionContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterRegexExpression(this); @@ -726,7 +726,7 @@ public ValueExpressionContext valueExpression(int i) { public TerminalNode COMMA(int i) { return getToken(EsqlBaseParser.COMMA, i); } - public LogicalInContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public LogicalInContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterLogicalIn(this); @@ -754,7 +754,7 @@ public BooleanExpressionContext booleanExpression(int i) { } public TerminalNode AND() { return getToken(EsqlBaseParser.AND, 0); } public TerminalNode OR() { return getToken(EsqlBaseParser.OR, 0); } - public LogicalBinaryContext(BooleanExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public LogicalBinaryContext(BooleanExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterLogicalBinary(this); @@ -1056,7 +1056,7 @@ public static class ValueExpressionDefaultContext extends ValueExpressionContext public OperatorExpressionContext operatorExpression() { return getRuleContext(OperatorExpressionContext.class,0); } - public ValueExpressionDefaultContext(ValueExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ValueExpressionDefaultContext(ValueExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterValueExpressionDefault(this); @@ -1084,7 +1084,7 @@ public List operatorExpression() { public OperatorExpressionContext operatorExpression(int i) { return getRuleContext(OperatorExpressionContext.class,i); } - public ComparisonContext(ValueExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ComparisonContext(ValueExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterComparison(this); @@ -1157,7 +1157,7 @@ public static class OperatorExpressionDefaultContext extends OperatorExpressionC public PrimaryExpressionContext primaryExpression() { return getRuleContext(PrimaryExpressionContext.class,0); } - public OperatorExpressionDefaultContext(OperatorExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public OperatorExpressionDefaultContext(OperatorExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterOperatorExpressionDefault(this); @@ -1188,7 +1188,7 @@ public OperatorExpressionContext operatorExpression(int i) { public TerminalNode PERCENT() { return getToken(EsqlBaseParser.PERCENT, 0); } public TerminalNode PLUS() { return getToken(EsqlBaseParser.PLUS, 0); } public TerminalNode MINUS() { return getToken(EsqlBaseParser.MINUS, 0); } - public ArithmeticBinaryContext(OperatorExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ArithmeticBinaryContext(OperatorExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterArithmeticBinary(this); @@ -1211,7 +1211,7 @@ public OperatorExpressionContext operatorExpression() { } public TerminalNode MINUS() { return getToken(EsqlBaseParser.MINUS, 0); } public TerminalNode PLUS() { return getToken(EsqlBaseParser.PLUS, 0); } - public ArithmeticUnaryContext(OperatorExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ArithmeticUnaryContext(OperatorExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterArithmeticUnary(this); @@ -1370,7 +1370,7 @@ public static class DereferenceContext extends PrimaryExpressionContext { public QualifiedNameContext qualifiedName() { return getRuleContext(QualifiedNameContext.class,0); } - public DereferenceContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public DereferenceContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterDereference(this); @@ -1390,7 +1390,7 @@ public static class ConstantDefaultContext extends PrimaryExpressionContext { public ConstantContext constant() { return getRuleContext(ConstantContext.class,0); } - public ConstantDefaultContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ConstantDefaultContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterConstantDefault(this); @@ -1412,7 +1412,7 @@ public BooleanExpressionContext booleanExpression() { return getRuleContext(BooleanExpressionContext.class,0); } public TerminalNode RP() { return getToken(EsqlBaseParser.RP, 0); } - public ParenthesizedExpressionContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ParenthesizedExpressionContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterParenthesizedExpression(this); @@ -1444,7 +1444,7 @@ public BooleanExpressionContext booleanExpression(int i) { public TerminalNode COMMA(int i) { return getToken(EsqlBaseParser.COMMA, i); } - public FunctionExpressionContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public FunctionExpressionContext(PrimaryExpressionContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterFunctionExpression(this); @@ -2355,7 +2355,7 @@ public BooleanValueContext booleanValue(int i) { public TerminalNode COMMA(int i) { return getToken(EsqlBaseParser.COMMA, i); } - public BooleanArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public BooleanArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterBooleanArrayLiteral(this); @@ -2375,7 +2375,7 @@ public static class DecimalLiteralContext extends ConstantContext { public DecimalValueContext decimalValue() { return getRuleContext(DecimalValueContext.class,0); } - public DecimalLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public DecimalLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterDecimalLiteral(this); @@ -2393,7 +2393,7 @@ public T accept(ParseTreeVisitor visitor) { @SuppressWarnings("CheckReturnValue") public static class NullLiteralContext extends ConstantContext { public TerminalNode NULL() { return getToken(EsqlBaseParser.NULL, 0); } - public NullLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public NullLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterNullLiteral(this); @@ -2414,7 +2414,7 @@ public IntegerValueContext integerValue() { return getRuleContext(IntegerValueContext.class,0); } public TerminalNode UNQUOTED_IDENTIFIER() { return getToken(EsqlBaseParser.UNQUOTED_IDENTIFIER, 0); } - public QualifiedIntegerLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public QualifiedIntegerLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterQualifiedIntegerLiteral(this); @@ -2443,7 +2443,7 @@ public StringContext string(int i) { public TerminalNode COMMA(int i) { return getToken(EsqlBaseParser.COMMA, i); } - public StringArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public StringArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterStringArrayLiteral(this); @@ -2463,7 +2463,7 @@ public static class StringLiteralContext extends ConstantContext { public StringContext string() { return getRuleContext(StringContext.class,0); } - public StringLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public StringLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterStringLiteral(this); @@ -2492,7 +2492,7 @@ public NumericValueContext numericValue(int i) { public TerminalNode COMMA(int i) { return getToken(EsqlBaseParser.COMMA, i); } - public NumericArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public NumericArrayLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterNumericArrayLiteral(this); @@ -2510,7 +2510,7 @@ public T accept(ParseTreeVisitor visitor) { @SuppressWarnings("CheckReturnValue") public static class InputParamContext extends ConstantContext { public TerminalNode PARAM() { return getToken(EsqlBaseParser.PARAM, 0); } - public InputParamContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public InputParamContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterInputParam(this); @@ -2530,7 +2530,7 @@ public static class IntegerLiteralContext extends ConstantContext { public IntegerValueContext integerValue() { return getRuleContext(IntegerValueContext.class,0); } - public IntegerLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public IntegerLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterIntegerLiteral(this); @@ -2550,7 +2550,7 @@ public static class BooleanLiteralContext extends ConstantContext { public BooleanValueContext booleanValue() { return getRuleContext(BooleanValueContext.class,0); } - public BooleanLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public BooleanLiteralContext(ConstantContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterBooleanLiteral(this); @@ -4003,7 +4003,7 @@ public void copyFrom(ShowCommandContext ctx) { public static class ShowInfoContext extends ShowCommandContext { public TerminalNode SHOW() { return getToken(EsqlBaseParser.SHOW, 0); } public TerminalNode INFO() { return getToken(EsqlBaseParser.INFO, 0); } - public ShowInfoContext(ShowCommandContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ShowInfoContext(ShowCommandContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterShowInfo(this); @@ -4022,7 +4022,7 @@ public T accept(ParseTreeVisitor visitor) { public static class ShowFunctionsContext extends ShowCommandContext { public TerminalNode SHOW() { return getToken(EsqlBaseParser.SHOW, 0); } public TerminalNode FUNCTIONS() { return getToken(EsqlBaseParser.FUNCTIONS, 0); } - public ShowFunctionsContext(ShowCommandContext ctx) { copyFrom(ctx); } + @SuppressWarnings("this-escape") public ShowFunctionsContext(ShowCommandContext ctx) { copyFrom(ctx); } @Override public void enterRule(ParseTreeListener listener) { if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterShowFunctions(this); diff --git a/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java b/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java index 0f79dfd277971..d4964f14b3fca 100644 --- a/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java +++ b/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java @@ -26,6 +26,7 @@ import static org.hamcrest.Matchers.instanceOf; public class RestGraphActionTests extends RestActionTestCase { + @SuppressWarnings("this-escape") private final List compatibleMediaType = Collections.singletonList(randomCompatibleMediaType(RestApiVersion.V_7)); @Before diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleService.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleService.java index b7e80cd1a9ab9..c2e2c80998992 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleService.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleService.java @@ -90,6 +90,7 @@ public class IndexLifecycleService private final LongSupplier nowSupplier; private SchedulerEngine.Job scheduledJob; + @SuppressWarnings("this-escape") public IndexLifecycleService( Settings settings, Client client, diff --git a/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java b/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java index 21631d597f8ae..97c17d18d7164 100644 --- a/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java +++ b/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java @@ -121,6 +121,7 @@ public static class Builder extends FieldMapper.Builder { private final Parameter ignoreMalformed; + @SuppressWarnings("this-escape") private final Parameter> metrics = new Parameter<>(Names.METRICS, false, () -> Defaults.METRICS, (n, c, o) -> { @SuppressWarnings("unchecked") List metricsList = (List) o; diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java index 4e04f85383def..ca23799300f24 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java @@ -99,6 +99,7 @@ public Builder(String name, Settings settings, IndexMode mode) { this(name, IGNORE_MALFORMED_SETTING.get(settings), mode); } + @SuppressWarnings("this-escape") public Builder(String name, boolean ignoreMalformedByDefault, IndexMode mode) { super(name); this.ignoreMalformed = Parameter.explicitBoolParam( diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java index 3b44ee8ba1d43..3c5b5c4243c5e 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java @@ -85,6 +85,7 @@ public class MlInitializationService implements ClusterStateListener { } // For testing + @SuppressWarnings("this-escape") public MlInitializationService( Client client, ThreadPool threadPool, diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingDeciderService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingDeciderService.java index b006f5728fcd2..11b0633ee7209 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingDeciderService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingDeciderService.java @@ -46,6 +46,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L private volatile boolean isMaster; private volatile int allocatedProcessorsScale; + @SuppressWarnings("this-escape") public MlAutoscalingDeciderService( MlMemoryTracker memoryTracker, Settings settings, @@ -55,6 +56,7 @@ public MlAutoscalingDeciderService( this(new NodeLoadDetector(memoryTracker), settings, nodeAvailabilityZoneMapper, clusterService, System::currentTimeMillis); } + @SuppressWarnings("this-escape") MlAutoscalingDeciderService( NodeLoadDetector nodeLoadDetector, Settings settings, diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java index dad3d820278e7..c7b342ab53cc8 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java @@ -39,6 +39,7 @@ public class CleanerService extends AbstractLifecycleComponent { private volatile TimeValue globalRetention; + @SuppressWarnings("this-escape") CleanerService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool, ExecutionScheduler executionScheduler) { this.threadPool = threadPool; this.genericExecutor = threadPool.generic(); @@ -50,6 +51,7 @@ public class CleanerService extends AbstractLifecycleComponent { clusterSettings.addSettingsUpdateConsumer(MonitoringField.HISTORY_DURATION, this::setGlobalRetention); } + @SuppressWarnings("this-escape") public CleanerService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool) { this(settings, clusterSettings, threadPool, new DefaultExecutionScheduler()); } diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java index 1d0cefa4734ac..9e0067c568979 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java @@ -51,6 +51,7 @@ public class Exporters extends AbstractLifecycleComponent { private final XPackLicenseState licenseState; private final ThreadContext threadContext; + @SuppressWarnings("this-escape") public Exporters( Settings settings, Map factories, diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporter.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporter.java index 266502d91027c..f753de62ba157 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporter.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporter.java @@ -108,6 +108,7 @@ public class LocalExporter extends Exporter implements ClusterStateListener, Cle private long stateInitializedTime; + @SuppressWarnings("this-escape") public LocalExporter( Exporter.Config config, Client client, diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpResourceTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpResourceTests.java index 5c766336d560b..cbf1d606e3a54 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpResourceTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpResourceTests.java @@ -28,6 +28,7 @@ */ public class HttpResourceTests extends ESTestCase { + @SuppressWarnings("this-escape") private final String owner = getTestName(); private final RestClient mockClient = mock(RestClient.class); diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/NodeFailureListenerTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/NodeFailureListenerTests.java index 1f97a736a11d2..464fd052f5425 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/NodeFailureListenerTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/NodeFailureListenerTests.java @@ -22,6 +22,7 @@ public class NodeFailureListenerTests extends ESTestCase { private final Sniffer sniffer = mock(Sniffer.class); + @SuppressWarnings("this-escape") private final HttpResource resource = new MockHttpResource(getTestName(), false); private final Node node = new Node(new HttpHost("localhost", 9200)); diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/WatcherExistsHttpResourceTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/WatcherExistsHttpResourceTests.java index 5337e71b65404..f341a1fadc226 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/WatcherExistsHttpResourceTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/WatcherExistsHttpResourceTests.java @@ -38,6 +38,7 @@ public class WatcherExistsHttpResourceTests extends AbstractPublishableHttpResou private final MultiHttpResource mockWatches = mock(MultiHttpResource.class); private final WatcherExistsHttpResource resource = new WatcherExistsHttpResource(owner, clusterService, mockWatches); + @SuppressWarnings("this-escape") private final Map expectedParameters = getParameters(resource.getDefaultParameters(), GET_EXISTS, XPACK_DOES_NOT_EXIST); public void testDoCheckIgnoresClientWhenNotElectedMaster() { diff --git a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/FailShardsOnInvalidLicenseClusterListener.java b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/FailShardsOnInvalidLicenseClusterListener.java index 0cd27510ddb6c..afc13567e59b7 100644 --- a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/FailShardsOnInvalidLicenseClusterListener.java +++ b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/FailShardsOnInvalidLicenseClusterListener.java @@ -38,6 +38,7 @@ public class FailShardsOnInvalidLicenseClusterListener implements LicenseStateLi private boolean allowed; + @SuppressWarnings("this-escape") public FailShardsOnInvalidLicenseClusterListener(XPackLicenseState xPackLicenseState, RerouteService rerouteService) { this.xPackLicenseState = xPackLicenseState; this.rerouteService = rerouteService; diff --git a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/codecs/lucene60/Lucene60MetadataOnlyPointsReader.java b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/codecs/lucene60/Lucene60MetadataOnlyPointsReader.java index 96170a946d6b3..8d7f81d28579c 100644 --- a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/codecs/lucene60/Lucene60MetadataOnlyPointsReader.java +++ b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/codecs/lucene60/Lucene60MetadataOnlyPointsReader.java @@ -41,6 +41,7 @@ public class Lucene60MetadataOnlyPointsReader extends PointsReader { final Map readers = new HashMap<>(); /** Sole constructor */ + @SuppressWarnings("this-escape") public Lucene60MetadataOnlyPointsReader(SegmentReadState readState) throws IOException { this.readState = readState; diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/extractor/AbstractFieldHitExtractor.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/extractor/AbstractFieldHitExtractor.java index 8dfe040a839bb..2e0caf0ff1aba 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/extractor/AbstractFieldHitExtractor.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/extractor/AbstractFieldHitExtractor.java @@ -69,6 +69,7 @@ protected AbstractFieldHitExtractor( } } + @SuppressWarnings("this-escape") protected AbstractFieldHitExtractor(StreamInput in) throws IOException { fieldName = in.readString(); String typeName = in.readOptionalString(); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/AttributeMap.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/AttributeMap.java index ff9e93272d21a..01ff4d67b1027 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/AttributeMap.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/AttributeMap.java @@ -169,6 +169,7 @@ public AttributeMap() { delegate = new LinkedHashMap<>(); } + @SuppressWarnings("this-escape") public AttributeMap(Attribute key, E value) { delegate = new LinkedHashMap<>(); add(key, value); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/ExpressionSet.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/ExpressionSet.java index eb229ce84652a..7461a3f70a338 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/ExpressionSet.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/ExpressionSet.java @@ -34,6 +34,7 @@ public ExpressionSet() { super(); } + @SuppressWarnings("this-escape") public ExpressionSet(Collection c) { addAll(c); } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/UnresolvedAttribute.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/UnresolvedAttribute.java index 1135bf988280b..f0bcd7f2a1517 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/UnresolvedAttribute.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/UnresolvedAttribute.java @@ -35,6 +35,7 @@ public UnresolvedAttribute(Source source, String name, String qualifier, String this(source, name, qualifier, null, unresolvedMessage, null); } + @SuppressWarnings("this-escape") public UnresolvedAttribute( Source source, String name, diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/FunctionRegistry.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/FunctionRegistry.java index ae6fba5d094d6..676681aa2a626 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/FunctionRegistry.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/FunctionRegistry.java @@ -47,10 +47,12 @@ public FunctionRegistry() {} /** * Register the given function definitions with this registry. */ + @SuppressWarnings("this-escape") public FunctionRegistry(FunctionDefinition... functions) { register(functions); } + @SuppressWarnings("this-escape") public FunctionRegistry(FunctionDefinition[]... groupFunctions) { register(groupFunctions); } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/fulltext/StringQueryPredicate.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/fulltext/StringQueryPredicate.java index 0a6707893ef2e..efb6809ae428c 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/fulltext/StringQueryPredicate.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/fulltext/StringQueryPredicate.java @@ -19,6 +19,7 @@ public class StringQueryPredicate extends FullTextPredicate { private final Map fields; + @SuppressWarnings("this-escape") public StringQueryPredicate(Source source, String query, String options) { super(source, query, options, emptyList()); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/RemoteClusterResolver.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/RemoteClusterResolver.java index d7db72808c940..28719b279614a 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/RemoteClusterResolver.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/RemoteClusterResolver.java @@ -19,6 +19,7 @@ public class RemoteClusterResolver extends RemoteClusterAware { private final CopyOnWriteArraySet clusters; + @SuppressWarnings("this-escape") public RemoteClusterResolver(Settings settings, ClusterSettings clusterSettings) { super(settings); clusters = new CopyOnWriteArraySet<>(getEnabledRemoteClusters(settings)); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetResultsAction.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetResultsAction.java index ee853ef017323..656be951895bf 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetResultsAction.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetResultsAction.java @@ -37,6 +37,7 @@ public abstract class AbstractTransportQlAsyncGetResultsAction> resultsService; private final TransportService transportService; + @SuppressWarnings("this-escape") public AbstractTransportQlAsyncGetResultsAction( String actionName, TransportService transportService, diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetStatusAction.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetStatusAction.java index 60faaebddb4f0..ecf161a96dd17 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetStatusAction.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plugin/AbstractTransportQlAsyncGetStatusAction.java @@ -42,6 +42,7 @@ public abstract class AbstractTransportQlAsyncGetStatusAction< private final Class asyncTaskClass; private final AsyncTaskIndexService> store; + @SuppressWarnings("this-escape") public AbstractTransportQlAsyncGetStatusAction( String actionName, TransportService transportService, diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/ScriptQuery.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/ScriptQuery.java index e44b97421e3a2..241b05392aef6 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/ScriptQuery.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/querydsl/query/ScriptQuery.java @@ -19,6 +19,7 @@ public class ScriptQuery extends LeafQuery { private final ScriptTemplate script; + @SuppressWarnings("this-escape") public ScriptQuery(Source source, ScriptTemplate script) { super(source); // make script null safe diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java index bff433d52135e..3f23e62bad729 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java @@ -225,6 +225,7 @@ public class ApiKeyService { private final AtomicLong lastEvictionCheckedAt = new AtomicLong(0); private final LongAdder evictionCounter = new LongAdder(); + @SuppressWarnings("this-escape") public ApiKeyService( Settings settings, Clock clock, diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java index 48c61c1d3b603..0dd9bb6f551d4 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java @@ -79,6 +79,7 @@ public class Realms extends AbstractLifecycleComponent implements Iterable activeRealms; + @SuppressWarnings("this-escape") public Realms( Settings settings, Environment env, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/LocalStateSecurity.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/LocalStateSecurity.java index 63ffc82ef287d..d44e7c27d760e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/LocalStateSecurity.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/LocalStateSecurity.java @@ -74,6 +74,7 @@ protected List infoActions() { } } + @SuppressWarnings("this-escape") public LocalStateSecurity(final Settings settings, final Path configPath) throws Exception { super(settings, configPath); LocalStateSecurity thisVar = this; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/UnstableLocalStateSecurity.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/UnstableLocalStateSecurity.java index e6321af58c56d..fcae0fa6c09f6 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/UnstableLocalStateSecurity.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/UnstableLocalStateSecurity.java @@ -33,6 +33,7 @@ */ public class UnstableLocalStateSecurity extends LocalStateSecurity { + @SuppressWarnings("this-escape") public UnstableLocalStateSecurity(Settings settings, Path configPath) throws Exception { super(settings, configPath); // We reuse most of the initialization of LocalStateSecurity, we then just overwrite diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/DummyUsernamePasswordRealm.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/DummyUsernamePasswordRealm.java index a8fbb5d2f0ed3..5c53179b5aa9f 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/DummyUsernamePasswordRealm.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/DummyUsernamePasswordRealm.java @@ -25,6 +25,7 @@ public class DummyUsernamePasswordRealm extends UsernamePasswordRealm { private Map> users; + @SuppressWarnings("this-escape") public DummyUsernamePasswordRealm(RealmConfig config) { super(config); initRealmRef( diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/NodeSeenService.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/NodeSeenService.java index 3996707d7552f..f9d8f69d888c0 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/NodeSeenService.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/NodeSeenService.java @@ -40,6 +40,7 @@ public class NodeSeenService implements ClusterStateListener { private final MasterServiceTaskQueue setSeenTaskQueue; + @SuppressWarnings("this-escape") public NodeSeenService(ClusterService clusterService) { this.clusterService = clusterService; this.setSeenTaskQueue = clusterService.createTaskQueue( diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SlmHealthIndicatorService.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SlmHealthIndicatorService.java index 442557f17a562..bb1e08b9561a5 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SlmHealthIndicatorService.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SlmHealthIndicatorService.java @@ -83,6 +83,7 @@ static Diagnosis.Definition checkRecentlyFailedSnapshots(String causeText, Strin private final ClusterService clusterService; private volatile long failedSnapshotWarnThreshold; + @SuppressWarnings("this-escape") public SlmHealthIndicatorService(ClusterService clusterService) { this.clusterService = clusterService; this.failedSnapshotWarnThreshold = clusterService.getClusterSettings().get(SLM_HEALTH_FAILED_SNAPSHOT_WARN_THRESHOLD_SETTING); diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridAggregationBuilder.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridAggregationBuilder.java index b3aa1f5e440d8..8bf939aeae494 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridAggregationBuilder.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridAggregationBuilder.java @@ -51,6 +51,7 @@ static int parsePrecision(XContentParser parser) throws IOException, Elasticsear return XContentMapValues.nodeIntegerValue(node); } + @SuppressWarnings("this-escape") public GeoHexGridAggregationBuilder(String name) { super(name); precision(DEFAULT_PRECISION); diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/metrics/CartesianBoundsAggregatorBase.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/metrics/CartesianBoundsAggregatorBase.java index 3121114d36ed4..ee2f59380bed8 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/metrics/CartesianBoundsAggregatorBase.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/metrics/CartesianBoundsAggregatorBase.java @@ -29,6 +29,7 @@ public abstract class CartesianBoundsAggregatorBase extends MetricsAggregator { private DoubleArray lefts; private DoubleArray rights; + @SuppressWarnings("this-escape") public CartesianBoundsAggregatorBase(String name, AggregationContext context, Aggregator parent, Map metadata) throws IOException { super(name, context, parent, metadata); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java index 6a1477cf05d3c..9636129c5f25c 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java @@ -66,6 +66,7 @@ public class EmbeddedCli implements Closeable { */ private boolean closed = false; + @SuppressWarnings("this-escape") public EmbeddedCli(String elasticsearchAddress, boolean checkConnectionOnStartup, @Nullable SecurityConfig security) throws IOException { PipedOutputStream outgoing = new PipedOutputStream(); diff --git a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/ConnectionConfiguration.java b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/ConnectionConfiguration.java index 6c7f730baebb1..d3e54a53729ee 100644 --- a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/ConnectionConfiguration.java +++ b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/ConnectionConfiguration.java @@ -116,6 +116,7 @@ public class ConnectionConfiguration { private final boolean allowPartialSearchResults; + @SuppressWarnings("this-escape") public ConnectionConfiguration(URI baseURI, String connectionString, Properties props) throws ClientException { this.connectionString = connectionString; Properties settings = props != null ? props : new Properties(); diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/RequestInfo.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/RequestInfo.java index 3337c23208813..52e14ca1fd2be 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/RequestInfo.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/RequestInfo.java @@ -46,12 +46,14 @@ public RequestInfo(Mode mode, String clientId) { this(mode, clientId, null); } + @SuppressWarnings("this-escape") public RequestInfo(Mode mode, String clientId, String version) { mode(mode); clientId(clientId); version(version); } + @SuppressWarnings("this-escape") public RequestInfo(Mode mode, SqlVersion version) { mode(mode); this.version = version; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index 3112544905396..d7b95f80298e2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -114,6 +114,7 @@ public class Analyzer extends ParameterizedRuleExecutor { XPackLicenseState licenseState = getLicenseState(); switch (mode) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java index 2933f1ed2a817..ac32a346f0625 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java @@ -74,6 +74,7 @@ public class TransportSqlQueryAction extends HandledTransportAction asyncTaskManagementService; + @SuppressWarnings("this-escape") @Inject public TransportSqlQueryAction( Settings settings, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java index 95f5747935217..9294aef87526d 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java @@ -127,6 +127,7 @@ private enum RunState { protected volatile boolean indexerThreadShuttingDown = false; protected volatile boolean saveStateRequestedDuringIndexerThreadShutdown = false; + @SuppressWarnings("this-escape") public TransformIndexer( ThreadPool threadPool, TransformServices transformServices, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java index 8478525cb4a53..d6f5efe9a0b13 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java @@ -70,6 +70,7 @@ public class TransformTask extends AllocatedPersistentTask implements TransformS private final TransformContext context; private final SetOnce indexer = new SetOnce<>(); + @SuppressWarnings("this-escape") public TransformTask( long id, String type, diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/input/InputRegistry.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/input/InputRegistry.java index 0f022019911c4..a6139cc93fe5e 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/input/InputRegistry.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/input/InputRegistry.java @@ -21,6 +21,7 @@ public class InputRegistry { private final Map> factories; + @SuppressWarnings("this-escape") public InputRegistry(Map> factories) { Map> map = new HashMap<>(factories); map.put(ChainInput.TYPE, new ChainInputFactory(this)); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/NotificationService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/NotificationService.java index 4e62e4152b4e8..62c78659ad5a7 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/NotificationService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/NotificationService.java @@ -48,6 +48,7 @@ public abstract class NotificationService { // using the new updated cluster settings private volatile SecureSettings cachedSecureSettings; + @SuppressWarnings("this-escape") public NotificationService( String type, Settings settings, diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/WebhookService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/WebhookService.java index dac784900abc7..e21490f180af4 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/WebhookService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/WebhookService.java @@ -68,6 +68,7 @@ public class WebhookService extends NotificationService { private final SSLService sslService; private volatile Set allowedDomains; + @SuppressWarnings("this-escape") public EmailService(Settings settings, @Nullable CryptoService cryptoService, SSLService sslService, ClusterSettings clusterSettings) { super("email", settings, clusterSettings, EmailService.getDynamicSettings(), EmailService.getSecureSettings()); this.cryptoService = cryptoService; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParser.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParser.java index 4ce302628d99c..2bb25436e056b 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParser.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParser.java @@ -125,6 +125,7 @@ public static List> getSettings() { private boolean warningEnabled = REPORT_WARNING_ENABLED_SETTING.getDefault(Settings.EMPTY); private final Map customWarnings = new ConcurrentHashMap<>(1); + @SuppressWarnings("this-escape") public ReportingAttachmentParser( Settings settings, WebhookService webhookService, diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/jira/JiraService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/jira/JiraService.java index d0aaeca17c7af..1cb59c0444b1b 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/jira/JiraService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/jira/JiraService.java @@ -64,6 +64,7 @@ public class JiraService extends NotificationService { private final HttpClient httpClient; + @SuppressWarnings("this-escape") public JiraService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) { super("jira", settings, clusterSettings, JiraService.getDynamicSettings(), JiraService.getSecureSettings()); this.httpClient = httpClient; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/PagerDutyService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/PagerDutyService.java index 1976018ddb9c1..dde225904fd0d 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/PagerDutyService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/PagerDutyService.java @@ -44,6 +44,7 @@ public class PagerDutyService extends NotificationService { private final HttpClient httpClient; + @SuppressWarnings("this-escape") public PagerDutyService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) { super("pagerduty", settings, clusterSettings, PagerDutyService.getDynamicSettings(), PagerDutyService.getSecureSettings()); this.httpClient = httpClient; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SlackService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SlackService.java index 33df0ad473b25..15c29e8dd2f93 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SlackService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SlackService.java @@ -48,6 +48,7 @@ public class SlackService extends NotificationService { private final HttpClient httpClient; + @SuppressWarnings("this-escape") public SlackService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) { super("slack", settings, clusterSettings, SlackService.getDynamicSettings(), SlackService.getSecureSettings()); this.httpClient = httpClient; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/DayTimes.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/DayTimes.java index 92f48543499f5..5d20db35dc7af 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/DayTimes.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/DayTimes.java @@ -36,10 +36,12 @@ public DayTimes(int hour, int minute) { this(new int[] { hour }, new int[] { minute }); } + @SuppressWarnings("this-escape") public DayTimes(int[] hour, int[] minute) { this(null, hour, minute); } + @SuppressWarnings("this-escape") DayTimes(String time, int[] hour, int[] minute) { this.time = time; this.hour = hour; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/MonthTimes.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/MonthTimes.java index a4f39b06c50bc..b01a786316e5d 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/MonthTimes.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/MonthTimes.java @@ -37,6 +37,7 @@ public MonthTimes() { this(DEFAULT_DAYS, DEFAULT_TIMES); } + @SuppressWarnings("this-escape") public MonthTimes(int[] days, DayTimes[] times) { this.days = days.length == 0 ? DEFAULT_DAYS : days; Arrays.sort(this.days); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/YearTimes.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/YearTimes.java index e31b8fd07ee10..a2091295820f9 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/YearTimes.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/support/YearTimes.java @@ -38,6 +38,7 @@ public YearTimes() { this(DEFAULT_MONTHS, DEFAULT_DAYS, DEFAULT_TIMES); } + @SuppressWarnings("this-escape") public YearTimes(EnumSet months, int[] days, DayTimes[] times) { this.months = months.isEmpty() ? DEFAULT_MONTHS : months; this.days = days.length == 0 ? DEFAULT_DAYS : days; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatchExecutionContextMockBuilder.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatchExecutionContextMockBuilder.java index 5faf3b78ca183..359aa2b28f660 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatchExecutionContextMockBuilder.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatchExecutionContextMockBuilder.java @@ -27,6 +27,7 @@ public class WatchExecutionContextMockBuilder { private final WatchExecutionContext ctx; private final Watch watch; + @SuppressWarnings("this-escape") public WatchExecutionContextMockBuilder(String watchId) { ctx = mock(WatchExecutionContext.class); watch = mock(Watch.class); diff --git a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java index e38ee029bd123..e6057d3b069aa 100644 --- a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java +++ b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java @@ -120,6 +120,7 @@ public TokenStreamComponents createComponents(String fieldName) { }); public static class PunctuationFoldingFilter extends TokenFilter { + @SuppressWarnings("this-escape") private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); /** diff --git a/x-pack/qa/security-example-spi-extension/src/main/java/org/elasticsearch/example/realm/CustomRoleMappingRealm.java b/x-pack/qa/security-example-spi-extension/src/main/java/org/elasticsearch/example/realm/CustomRoleMappingRealm.java index 1c46fb3a88ecd..d013ce143a673 100644 --- a/x-pack/qa/security-example-spi-extension/src/main/java/org/elasticsearch/example/realm/CustomRoleMappingRealm.java +++ b/x-pack/qa/security-example-spi-extension/src/main/java/org/elasticsearch/example/realm/CustomRoleMappingRealm.java @@ -38,6 +38,7 @@ public class CustomRoleMappingRealm extends Realm implements CachingRealm { private final Cache cache; private final UserRoleMapper roleMapper; + @SuppressWarnings("this-escape") public CustomRoleMappingRealm(RealmConfig config, UserRoleMapper roleMapper) { super(config); this.cache = CacheBuilder.builder().build(); From 97a137835c2012d02db8859c137dd3695f24feda Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Mon, 25 Sep 2023 10:01:16 +0100 Subject: [PATCH 050/155] [ML] Adjust a memory limit in AutodetectMemoryLimitIT for Windows tests (#99858) Account for (another) slight increase in memory overhead due to changes to some Boost containers. This change addresses an integration test failure on Windows - hence was not picked up by the PR CI tests. --- .../xpack/ml/integration/AutodetectMemoryLimitIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java index 1fc807df55baa..5405852173a62 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java @@ -225,7 +225,7 @@ public void testManyDistinctOverFields() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(117500000L)); + assertThat(modelSizeStats.getModelBytes(), lessThan(120000000L)); assertThat(modelSizeStats.getModelBytes(), greaterThan(90000000L)); assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } From b3b0f374929e424c9402d1e111e45830937f6d20 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 25 Sep 2023 10:11:16 +0100 Subject: [PATCH 051/155] Add trace logging for clearing the DSL error store (#99822) --- .../DataStreamLifecycleErrorStore.java | 4 +-- .../lifecycle/DataStreamLifecycleService.java | 32 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleErrorStore.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleErrorStore.java index 21ef1811fe3a3..c0b517fa2ca9c 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleErrorStore.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleErrorStore.java @@ -36,10 +36,10 @@ public class DataStreamLifecycleErrorStore { */ @Nullable public String recordError(String indexName, Exception e) { - String exceptionToString = Strings.toString(((builder, params) -> { + String exceptionToString = Strings.toString((builder, params) -> { ElasticsearchException.generateThrowableXContent(builder, EMPTY_PARAMS, e); return builder; - })); + }); String recordedError = Strings.substring(exceptionToString, 0, MAX_ERROR_MESSAGE_LENGTH); return indexNameToError.put(indexName, recordedError); } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java index 3f07e9ea478df..3e2d5aae6db38 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java @@ -15,14 +15,19 @@ import org.elasticsearch.ResourceAlreadyExistsException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ResultDeduplicator; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeAction; import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest; import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse; +import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockAction; import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockRequest; import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockResponse; +import org.elasticsearch.action.admin.indices.rollover.RolloverAction; import org.elasticsearch.action.admin.indices.rollover.RolloverConfiguration; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction; import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.elasticsearch.action.downsample.DownsampleAction; import org.elasticsearch.action.downsample.DownsampleConfig; @@ -231,6 +236,7 @@ public void clusterChanged(ClusterChangedEvent event) { cancelJob(); // clear the deduplicator on master failover so we could re-send the requests in case we're re-elected transportActionsDeduplicator.clear(); + logger.trace("Clearing the error store as we are not the elected master anymore"); errorStore.clearStore(); } } @@ -242,6 +248,7 @@ public void close() { if (engine != null) { engine.stop(); } + logger.trace("Clearing the error store as we are closing"); errorStore.clearStore(); } @@ -477,6 +484,7 @@ private void downsampleIndexOnce(DataStreamLifecycle.Downsampling.Round round, S transportActionsDeduplicator.executeOnce( request, new ErrorRecordingActionListener( + DownsampleAction.NAME, sourceIndex, errorStore, Strings.format( @@ -567,12 +575,14 @@ private Set evaluateDownsampleStatus( * Issues a request to replace the backing index with the downsample index through the cluster state changes deduplicator. */ private void replaceBackingIndexWithDownsampleIndexOnce(DataStream dataStream, String backingIndexName, String downsampleIndexName) { + String requestName = "dsl-replace-" + dataStream.getName() + "-" + backingIndexName + "-" + downsampleIndexName; clusterStateChangesDeduplicator.executeOnce( // we use a String key here as otherwise it's ... awkward as we have to create the DeleteSourceAndAddDownsampleToDS as the // key _without_ a listener (passing in null) and then below we create it again with the `reqListener`. We're using a String // as it seems to be clearer. - "dsl-replace-" + dataStream.getName() + "-" + backingIndexName + "-" + downsampleIndexName, + requestName, new ErrorRecordingActionListener( + requestName, backingIndexName, errorStore, Strings.format( @@ -612,6 +622,7 @@ private void deleteIndexOnce(String indexName, String reason) { transportActionsDeduplicator.executeOnce( deleteIndexRequest, new ErrorRecordingActionListener( + DeleteIndexAction.NAME, indexName, errorStore, Strings.format("Data stream lifecycle encountered an error trying to delete index [%s]", indexName) @@ -628,6 +639,7 @@ private void addIndexBlockOnce(String indexName) { transportActionsDeduplicator.executeOnce( addIndexBlockRequest, new ErrorRecordingActionListener( + AddIndexBlockAction.NAME, indexName, errorStore, Strings.format("Data stream lifecycle service encountered an error trying to mark index [%s] as readonly", indexName) @@ -662,8 +674,10 @@ private void clearErrorStoreForUnmanagedIndices(DataStream dataStream) { for (String indexName : errorStore.getAllIndices()) { IndexMetadata indexMeta = metadata.index(indexName); if (indexMeta == null) { + logger.trace("Clearing recorded error for index [{}] because the index doesn't exist anymore", indexName); errorStore.clearRecordedError(indexName); } else if (dataStream.isIndexManagedByDataStreamLifecycle(indexMeta.getIndex(), metadata::index) == false) { + logger.trace("Clearing recorded error for index [{}] because the index is not managed by DSL anymore", indexName); errorStore.clearRecordedError(indexName); } } @@ -680,6 +694,7 @@ private void maybeExecuteRollover(ClusterState state, DataStream dataStream) { transportActionsDeduplicator.executeOnce( rolloverRequest, new ErrorRecordingActionListener( + RolloverAction.NAME, writeIndex.getName(), errorStore, Strings.format("Data stream lifecycle encountered an error trying to rollover data steam [%s]", dataStream.getName()) @@ -752,6 +767,7 @@ private Set maybeExecuteForceMerge(ClusterState state, List indice transportActionsDeduplicator.executeOnce( updateMergePolicySettingsRequest, new ErrorRecordingActionListener( + UpdateSettingsAction.NAME, indexName, errorStore, Strings.format( @@ -769,6 +785,7 @@ private Set maybeExecuteForceMerge(ClusterState state, List indice transportActionsDeduplicator.executeOnce( new ForceMergeRequestWrapper(forceMergeRequest), new ErrorRecordingActionListener( + ForceMergeAction.NAME, indexName, errorStore, Strings.format( @@ -853,6 +870,7 @@ public void onResponse(AcknowledgedResponse acknowledgedResponse) { public void onFailure(Exception e) { if (e instanceof IndexNotFoundException) { // index was already deleted, treat this as a success + logger.trace("Clearing recorded error for index [{}] because the index was deleted", targetIndex); errorStore.clearRecordedError(targetIndex); listener.onResponse(null); return; @@ -935,6 +953,7 @@ public void onResponse(AddIndexBlockResponse addIndexBlockResponse) { public void onFailure(Exception e) { if (e instanceof IndexNotFoundException) { // index was already deleted, treat this as a success + logger.trace("Clearing recorded error for index [{}] because the index was deleted", targetIndex); errorStore.clearRecordedError(targetIndex); listener.onResponse(null); return; @@ -1087,11 +1106,19 @@ static TimeValue getRetentionConfiguration(DataStream dataStream) { * target index using the clearErrorRecord callback. */ static class ErrorRecordingActionListener implements ActionListener { + + private final String actionName; private final String targetIndex; private final DataStreamLifecycleErrorStore errorStore; private final String errorLogMessage; - ErrorRecordingActionListener(String targetIndex, DataStreamLifecycleErrorStore errorStore, String errorLogMessage) { + ErrorRecordingActionListener( + String actionName, + String targetIndex, + DataStreamLifecycleErrorStore errorStore, + String errorLogMessage + ) { + this.actionName = actionName; this.targetIndex = targetIndex; this.errorStore = errorStore; this.errorLogMessage = errorLogMessage; @@ -1099,6 +1126,7 @@ static class ErrorRecordingActionListener implements ActionListener { @Override public void onResponse(Void unused) { + logger.trace("Clearing recorded error for index [{}] because the [{}] action was successful", targetIndex, actionName); errorStore.clearRecordedError(targetIndex); } From a5fa195c10eaa05aee2c20a0293d3a9010f32003 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Mon, 25 Sep 2023 10:31:59 +0100 Subject: [PATCH 052/155] Add checks in term and terms queries that input terms are not too long (#99818) Lucene indexes do not allow terms of greater than 32k bytes long. Any queries that contain terms that exceed this length will by definition not match anything, and can cause cluster instability by consuming large amounts of heap. They are also generally always a user error (for example, a termsquery that concatenates all its inputs into a single string rather than splitting them into json arrays). This commit adds some checking to Term and Terms query builders that will throw an exception if any of their input terms are greater than the maximum allowed length by the lucene IndexWriter. Fixes #99802 --- docs/changelog/99818.yaml | 6 ++++ .../common/lucene/BytesRefs.java | 31 +++++++++++++++++++ .../index/query/AbstractQueryBuilder.java | 4 +-- .../query/AbstractQueryBuilderTests.java | 8 +++++ .../index/query/TermQueryBuilderTests.java | 10 ++++++ .../index/query/TermsQueryBuilderTests.java | 21 +++++++++---- 6 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 docs/changelog/99818.yaml diff --git a/docs/changelog/99818.yaml b/docs/changelog/99818.yaml new file mode 100644 index 0000000000000..8835bcf28e050 --- /dev/null +++ b/docs/changelog/99818.yaml @@ -0,0 +1,6 @@ +pr: 99818 +summary: Add checks in term and terms queries that input terms are not too long +area: Search +type: enhancement +issues: + - 99802 diff --git a/server/src/main/java/org/elasticsearch/common/lucene/BytesRefs.java b/server/src/main/java/org/elasticsearch/common/lucene/BytesRefs.java index 78fd5a76045c5..7bae1976327df 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/BytesRefs.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/BytesRefs.java @@ -8,6 +8,7 @@ package org.elasticsearch.common.lucene; +import org.apache.lucene.index.IndexWriter; import org.apache.lucene.util.BytesRef; public class BytesRefs { @@ -39,4 +40,34 @@ public static BytesRef toBytesRef(Object value) { return new BytesRef(value.toString()); } + /** + * Checks that the input is not longer than {@link IndexWriter#MAX_TERM_LENGTH} + * @param input a BytesRef + * @return the same BytesRef, if no exception has been thrown + * @throws IllegalArgumentException if the input is too long + */ + public static BytesRef checkIndexableLength(BytesRef input) { + if (input.length > IndexWriter.MAX_TERM_LENGTH) { + throw new IllegalArgumentException( + "Term is longer than maximum indexable length, term starting with [" + safeStringPrefix(input, 10) + ); + } + return input; + } + + /** + * Produces a UTF-string prefix of the input BytesRef. If the prefix cutoff would produce + * ill-formed UTF, it falls back to the hexadecimal representation. + * @param input an input BytesRef + * @return a String prefix + */ + private static String safeStringPrefix(BytesRef input, int prefixLength) { + BytesRef prefix = new BytesRef(input.bytes, input.offset, prefixLength); + try { + return prefix.utf8ToString(); + } catch (Exception e) { + return prefix.toString(); + } + } + } diff --git a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java index 53eae2e86c39b..e9d2223029e14 100644 --- a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java @@ -212,9 +212,9 @@ public final int hashCode() { */ static Object maybeConvertToBytesRef(Object obj) { if (obj instanceof String) { - return BytesRefs.toBytesRef(obj); + return BytesRefs.checkIndexableLength(BytesRefs.toBytesRef(obj)); } else if (obj instanceof CharBuffer) { - return new BytesRef((CharBuffer) obj); + return BytesRefs.checkIndexableLength(new BytesRef((CharBuffer) obj)); } else if (obj instanceof BigInteger) { return BytesRefs.toBytesRef(obj); } diff --git a/server/src/test/java/org/elasticsearch/index/query/AbstractQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/AbstractQueryBuilderTests.java index b5e7dd7741392..3bf52eacb6df3 100644 --- a/server/src/test/java/org/elasticsearch/index/query/AbstractQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/AbstractQueryBuilderTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.index.query; +import org.apache.lucene.index.IndexWriter; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.search.SearchModule; @@ -22,6 +23,7 @@ import static java.util.Collections.emptyList; import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder; +import static org.hamcrest.Matchers.containsString; public class AbstractQueryBuilderTests extends ESTestCase { @@ -84,4 +86,10 @@ protected NamedXContentRegistry xContentRegistry() { return xContentRegistry; } + public void testMaybeConvertToBytesRefLongTerm() { + String longTerm = "a".repeat(IndexWriter.MAX_TERM_LENGTH + 1); + Exception e = expectThrows(IllegalArgumentException.class, () -> AbstractQueryBuilder.maybeConvertToBytesRef(longTerm)); + assertThat(e.getMessage(), containsString("term starting with [aaaaa")); + } + } diff --git a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java index 364a4f52f0fca..c8bfbe304918c 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.index.query; +import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.AutomatonQuery; import org.apache.lucene.search.MatchNoDocsQuery; @@ -20,9 +21,11 @@ import org.elasticsearch.xcontent.json.JsonStringEncoder; import java.io.IOException; +import java.util.Locale; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; public class TermQueryBuilderTests extends AbstractTermQueryTestCase { @@ -227,4 +230,11 @@ public void testMustRewrite() throws IOException { IllegalStateException e = expectThrows(IllegalStateException.class, () -> queryBuilder.toQuery(context)); assertEquals("Rewrite first", e.getMessage()); } + + public void testLongTerm() throws IOException { + String longTerm = "a".repeat(IndexWriter.MAX_TERM_LENGTH + 1); + Exception e = expectThrows(IllegalArgumentException.class, () -> parseQuery(String.format(Locale.ROOT, """ + { "term" : { "foo" : "%s" } }""", longTerm))); + assertThat(e.getMessage(), containsString("term starting with [aaaaa")); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java index 746331be581fc..6d43276c7bd20 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.index.query; +import org.apache.lucene.index.IndexWriter; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.MatchNoDocsQuery; @@ -35,6 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Objects; import static org.hamcrest.Matchers.containsString; @@ -71,7 +73,7 @@ protected TermsQueryBuilder doCreateTestQueryBuilder() { || choice.equals(INT_RANGE_FIELD_NAME) || choice.equals(DATE_RANGE_FIELD_NAME) || choice.equals(DATE_NANOS_FIELD_NAME), // TODO: needs testing for date_nanos type - () -> getRandomFieldName() + AbstractQueryTestCase::getRandomFieldName ); Object[] values = new Object[randomInt(5)]; for (int i = 0; i < values.length; i++) { @@ -95,7 +97,7 @@ private TermsLookup randomTermsLookup() { protected void doAssertLuceneQuery(TermsQueryBuilder queryBuilder, Query query, SearchExecutionContext context) throws IOException { if (queryBuilder.termsLookup() == null && (queryBuilder.values() == null || queryBuilder.values().isEmpty())) { assertThat(query, instanceOf(MatchNoDocsQuery.class)); - } else if (queryBuilder.termsLookup() != null && randomTerms.size() == 0) { + } else if (queryBuilder.termsLookup() != null && randomTerms.isEmpty()) { assertThat(query, instanceOf(MatchNoDocsQuery.class)); } else { assertThat( @@ -138,14 +140,14 @@ protected void doAssertLuceneQuery(TermsQueryBuilder queryBuilder, Query query, } } - public void testEmtpyFieldName() { + public void testEmptyFieldName() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new TermsQueryBuilder(null, "term")); assertEquals("field name cannot be null.", e.getMessage()); e = expectThrows(IllegalArgumentException.class, () -> new TermsQueryBuilder("", "term")); assertEquals("field name cannot be null.", e.getMessage()); } - public void testEmtpyTermsLookup() { + public void testEmptyTermsLookup() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new TermsQueryBuilder("field", (TermsLookup) null)); assertEquals("No value or termsLookup specified for terms query", e.getMessage()); } @@ -194,7 +196,7 @@ public GetResponse executeGet(GetRequest getRequest) { try { XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint(); builder.startObject(); - builder.array(termsPath, randomTerms.toArray(new Object[randomTerms.size()])); + builder.array(termsPath, randomTerms.toArray(Object[]::new)); builder.endObject(); json = Strings.toString(builder); } catch (IOException ex) { @@ -262,7 +264,7 @@ public void testMustRewrite() throws IOException { assertEquals("query must be rewritten first", e.getMessage()); // terms lookup removes null values - List nonNullTerms = randomTerms.stream().filter(x -> x != null).toList(); + List nonNullTerms = randomTerms.stream().filter(Objects::nonNull).toList(); QueryBuilder expected; if (nonNullTerms.isEmpty()) { expected = new MatchNoneQueryBuilder(); @@ -308,6 +310,13 @@ public void testRewriteIndexQueryToNotMatchNone() throws IOException { } } + public void testLongTerm() throws IOException { + String longTerm = "a".repeat(IndexWriter.MAX_TERM_LENGTH + 1); + Exception e = expectThrows(IllegalArgumentException.class, () -> parseQuery(String.format(Locale.getDefault(), """ + { "terms" : { "foo" : [ "q", "%s" ] } }""", longTerm))); + assertThat(e.getMessage(), containsString("term starting with [aaaaa")); + } + @Override protected QueryBuilder parseQuery(XContentParser parser) throws IOException { QueryBuilder query = super.parseQuery(parser); From 58268ed2c87fb086a46a670591d559ce2072191d Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Mon, 25 Sep 2023 11:37:06 +0200 Subject: [PATCH 053/155] Fix test number generation for UL testing (#99862) Fix random non-negative short and int generation. Closes #99861 --- .../elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java index fae53e6d214a6..1793369c14905 100644 --- a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java +++ b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java @@ -814,8 +814,8 @@ public void testGettingValidNumbersWithCastingFromUnsignedLong() throws IOExcept updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); byte randomNonNegativeByte = randomNonNegativeByte(); - short randomNonNegativeShort = (short) (Math.abs(randomShort()) - 1); - int randomNonNegativeInt = Math.abs(randomInt()) - 1; + short randomNonNegativeShort = (short) (randomShort() & Short.MAX_VALUE); + int randomNonNegativeInt = randomNonNegativeInt(); long randomNonNegativeLong = randomNonNegativeLong(); double randomNonNegativeFloat = (float) randomDoubleBetween(0, UNSIGNED_LONG_MAX.doubleValue(), true); double randomNonNegativeDouble = randomDoubleBetween(0, UNSIGNED_LONG_MAX.doubleValue(), true); From 923a9442141552d0896cbb7e42b4202b1da8eec6 Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:14:56 +0300 Subject: [PATCH 054/155] Fix cardinality agg for const_keyword (#99814) * Fix cardinality agg for const_keyword const_keyword fields don't show up in the leafReader, since they have a const value. #92060 modified the logic to return no results in case the leaf reader contains no information about the requested field in a cardinality aggregation. This is wrong for const_keyword fields, as they contain up to 1 distinct value. To fix this, we fall back to the old logic in this case that can handle const_keyword fields properly. Fixes #99776 * Update docs/changelog/99814.yaml * Update skip ranges for broken releases. --- docs/changelog/99814.yaml | 6 +++ .../GlobalOrdCardinalityAggregator.java | 9 ++--- .../test/constant_keyword/10_basic.yml | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 docs/changelog/99814.yaml diff --git a/docs/changelog/99814.yaml b/docs/changelog/99814.yaml new file mode 100644 index 0000000000000..1632be42b4e4c --- /dev/null +++ b/docs/changelog/99814.yaml @@ -0,0 +1,6 @@ +pr: 99814 +summary: Fix cardinality agg for `const_keyword` +area: Aggregations +type: bug +issues: + - 99776 diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/GlobalOrdCardinalityAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/GlobalOrdCardinalityAggregator.java index ee412666a21fa..5e59da1591270 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/GlobalOrdCardinalityAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/GlobalOrdCardinalityAggregator.java @@ -252,17 +252,14 @@ public CompetitiveIterator competitiveIterator() { } } else { final FieldInfo fi = aggCtx.getLeafReaderContext().reader().getFieldInfos().fieldInfo(field); - if (fi == null) { - // The field doesn't exist at all, we can skip the segment entirely - noData++; - return LeafBucketCollector.NO_OP_COLLECTOR; - } else if (fi.getIndexOptions() != IndexOptions.NONE) { + if (fi != null && fi.getIndexOptions() != IndexOptions.NONE) { // The field doesn't have terms while index options are not NONE. This means that this segment doesn't have a single // value for the field. noData++; return LeafBucketCollector.NO_OP_COLLECTOR; } - // Otherwise we might be aggregating e.g. an IP field, which indexes data using points rather than an inverted index. + // Otherwise we might be aggregating e.g. an IP or a const_keyword field, which index data using points rather than an + // inverted index. } } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/constant_keyword/10_basic.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/constant_keyword/10_basic.yml index 3c5f29178881d..a89b24ff45593 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/constant_keyword/10_basic.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/constant_keyword/10_basic.yml @@ -413,3 +413,40 @@ setup: - match: { aggregations.agg.buckets.1.0-bucket.doc_count: 0 } - match: { aggregations.agg.buckets.2.key: 200.0 } - match: { aggregations.agg.buckets.2.0-bucket.doc_count: 0 } + + +--- +Cardinality agg: + - skip: + version: " - 7.6.99, 8.9.00 - 8.10.99" + reason: "constant_keyword was added in 7.7, bug introduced in 8.9 and fixed in 8.11" + + - do: + indices.create: + index: test3 + body: + mappings: + properties: + test: + type: constant_keyword + value: value1 + + - do: + bulk: + index: test3 + refresh: true + body: | + {"index":{}} + { "test": "value1" } + + - do: + search: + index: test3 + body: + size: 0 + aggs: + card: + cardinality: + field: test + + - match: { aggregations.card.value: 1 } From db1081030966812dec534ebc36673551e845fec0 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 25 Sep 2023 12:25:10 +0200 Subject: [PATCH 055/155] Expression script engine cleanups around _value usage (#99706) We have found some inconsistencies as part of #99667, around the current usages of ReplaceableConstDoubleValueSource in ExpressionsScriptEngine. It looks like _value is exposed to the bindings of score scripts, but setValue is never called hence it will always be 0. That can be replaced with a constant double values source, but the next question is whether it even needs to be added to the bindings then. Another cleanup discussed in #99667 is throwing UnsupportedOperationException from ReplaceableConstDoubleValueSource#explain as it should never be called. Implementing the method means we need to test it which makes little sense if the method is never called in production code. --- .../script/expression/ExpressionScriptEngine.java | 4 +--- .../expression/ReplaceableConstDoubleValueSource.java | 9 ++------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java index 7870421817466..7d80a0d401013 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java @@ -371,7 +371,6 @@ private static ScoreScript.LeafFactory newScoreScript(Expression expr, SearchLoo // NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings, // instead of complicating SimpleBindings (which should stay simple) SimpleBindings bindings = new SimpleBindings(); - ReplaceableConstDoubleValueSource specialValue = null; boolean needsScores = false; for (String variable : expr.variables) { try { @@ -379,8 +378,7 @@ private static ScoreScript.LeafFactory newScoreScript(Expression expr, SearchLoo bindings.add("_score", DoubleValuesSource.SCORES); needsScores = true; } else if (variable.equals("_value")) { - specialValue = new ReplaceableConstDoubleValueSource(); - bindings.add("_value", specialValue); + bindings.add("_value", DoubleValuesSource.constant(0)); // noop: _value is special for aggregations, and is handled in ExpressionScriptBindings // TODO: if some uses it in a scoring expression, they will get a nasty failure when evaluating...need a // way to know this is for aggregations and so _value is ok to have... diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java index 903ddaf72340e..697eca6226e3c 100644 --- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java +++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ReplaceableConstDoubleValueSource.java @@ -38,13 +38,8 @@ public boolean needsScores() { } @Override - public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException { - // TODO where is this explain called? I bet it's never tested, and probably never called. - ReplaceableConstDoubleValues fv = specialValues.get(ctx); - if (fv.advanceExact(docId)) { - return Explanation.match((float) fv.doubleValue(), "ReplaceableConstDoubleValues"); - } - return Explanation.noMatch("ReplaceableConstDoubleValues"); + public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) { + throw new UnsupportedOperationException("explain is not supported for _value and should never be called"); } @Override From 1a48c59aa4ff90839f551d3f11d206e385a5351d Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Mon, 25 Sep 2023 12:05:17 +0100 Subject: [PATCH 056/155] Remove unhelpful BulkResponse#status() method (#99865) BulkResponse#status() always returns 200 OK, and consumers wishing to check if there were any errors during the bulk update need to consult the hasFailures() method. This is confusing and error-prone, as can be seen in a couple of tests that are checking status() to know whether or not to continue. This commit removes status() entirely from BulkResponse. Related to #99824 --- .../org/elasticsearch/action/bulk/BulkResponse.java | 10 ++-------- .../rest/action/document/RestBulkAction.java | 4 ++-- .../xpack/downsample/DownsampleShardIndexer.java | 3 ++- .../xpack/ml/integration/DeleteExpiredDataIT.java | 3 +-- .../full/SearchableSnapshotsPrewarmingIntegTests.java | 1 - 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/bulk/BulkResponse.java b/server/src/main/java/org/elasticsearch/action/bulk/BulkResponse.java index 0725e6c684abe..0ce472520a4fd 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/BulkResponse.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/BulkResponse.java @@ -12,9 +12,8 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.StatusToXContentObject; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; @@ -32,7 +31,7 @@ * bulk requests. Each item holds the index/type/id is operated on, and if it failed or not (with the * failure message). */ -public class BulkResponse extends ActionResponse implements Iterable, StatusToXContentObject { +public class BulkResponse extends ActionResponse implements Iterable, ToXContentObject { private static final String ITEMS = "items"; private static final String ERRORS = "errors"; @@ -134,11 +133,6 @@ public void writeTo(StreamOutput out) throws IOException { out.writeZLong(ingestTookInMillis); } - @Override - public RestStatus status() { - return RestStatus.OK; - } - @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java index 1a5a0990c27d8..9092686e083f6 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java @@ -19,7 +19,7 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestToXContentListener; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import java.io.IOException; @@ -93,7 +93,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC request.getRestApiVersion() ); - return channel -> client.bulk(bulkRequest, new RestStatusToXContentListener<>(channel)); + return channel -> client.bulk(bulkRequest, new RestToXContentListener<>(channel)); } @Override diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/DownsampleShardIndexer.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/DownsampleShardIndexer.java index 35af9d9cfb435..f74bd299916c1 100644 --- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/DownsampleShardIndexer.java +++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/DownsampleShardIndexer.java @@ -39,6 +39,7 @@ import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationExecutionContext; import org.elasticsearch.search.aggregations.BucketCollector; @@ -280,7 +281,7 @@ public void afterBulk(long executionId, BulkRequest request, BulkResponse respon bulkIngestTookMillis, bulkTookMillis, response.hasFailures(), - response.status().getStatus() + RestStatus.OK.getStatus() ) ); task.updateBulkInfo(bulkIngestTookMillis, bulkTookMillis); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java index 6ff46c0f098b5..15026a719590e 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java @@ -20,7 +20,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -290,7 +289,7 @@ private void testExpiredDeletion(Float customThrottle, int numUnusedState) throw } // Before we call the delete-expired-data action we need to make sure the unused state docs were indexed - assertThat(indexUnusedStateDocsResponse.get().status(), equalTo(RestStatus.OK)); + assertFalse(indexUnusedStateDocsResponse.get().hasFailures()); // Now call the action under test assertThat(deleteExpiredData(customThrottle).isDeleted(), is(true)); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java index 1cdfa51bdf776..8f3a0d9b64238 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java @@ -139,7 +139,6 @@ public void testConcurrentPrewarming() throws Exception { bulkRequest.add(client().prepareIndex(indexName).setSource("foo", randomBoolean() ? "bar" : "baz")); } final BulkResponse bulkResponse = bulkRequest.get(); - assertThat(bulkResponse.status(), is(RestStatus.OK)); assertThat(bulkResponse.hasFailures(), is(false)); } docsPerIndex.put(indexName, nbDocs); From 0fd0cc6f29ba99a4c8360fe9f041062a7d7f9d0f Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Mon, 25 Sep 2023 07:54:47 -0500 Subject: [PATCH 057/155] Changing DataStreamsRestIT to use a data stream wildcard query that works with or without security enabled (#99836) --- .../java/org/elasticsearch/datastreams/DataStreamsRestIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamsRestIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamsRestIT.java index e4a286ea6c237..a60d36b0460a5 100644 --- a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamsRestIT.java +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamsRestIT.java @@ -47,7 +47,7 @@ public void testHiddenDataStream() throws IOException { assertOK(client().performRequest(createDocRequest)); - Request getDataStreamsRequest = new Request("GET", "/_data_stream?expand_wildcards=hidden"); + Request getDataStreamsRequest = new Request("GET", "/_data_stream/*?expand_wildcards=hidden"); Response response = client().performRequest(getDataStreamsRequest); Map dataStreams = entityAsMap(response); assertEquals(Collections.singletonList("hidden"), XContentMapValues.extractValue("data_streams.name", dataStreams)); @@ -77,7 +77,7 @@ public void testHiddenDataStreamImplicitHiddenSearch() throws IOException { assertOK(client().performRequest(createDocRequest)); - Request getDataStreamsRequest = new Request("GET", "/_data_stream?expand_wildcards=hidden"); + Request getDataStreamsRequest = new Request("GET", "/_data_stream/*?expand_wildcards=hidden"); Response response = client().performRequest(getDataStreamsRequest); Map dataStreams = entityAsMap(response); assertEquals(Collections.singletonList(".hidden"), XContentMapValues.extractValue("data_streams.name", dataStreams)); From 917aa7a236b04e3cf9bb90a8425e4b9072fa9bd1 Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Mon, 25 Sep 2023 07:55:11 -0500 Subject: [PATCH 058/155] Avoiding using ILM settings in data streams yaml rest tests (#99837) --- .../datastreams/DataStreamsClientYamlTestSuiteIT.java | 1 - .../test/data_stream/80_resolve_index_data_streams.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java b/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java index 4a7fa6109f924..3089b778c69c6 100644 --- a/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java +++ b/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java @@ -40,7 +40,6 @@ protected Settings restClientSettings() { public static ElasticsearchCluster cluster = ElasticsearchCluster.local() .distribution(DistributionType.DEFAULT) .module("reindex") - .setting("indices.lifecycle.history_index_enabled", "false") .setting("xpack.security.enabled", "true") .keystore("bootstrap.password", "x-pack-test-password") .user("x_pack_rest_user", "x-pack-test-password") diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml index a534c8f5834ea..930b68b4361c8 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml @@ -116,7 +116,7 @@ setup: - do: indices.resolve_index: - name: ['*','-.ml*'] + name: ['*','-.ml*', '-.ds-ilm-history*'] expand_wildcards: [all] - match: {indices.0.name: "/\\.ds-simple-data-stream1-(\\d{4}\\.\\d{2}\\.\\d{2}-)?000001/"} From c5847412a68eb9915ba02986f2f5552948925c6e Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 25 Sep 2023 14:26:42 +0100 Subject: [PATCH 059/155] DSL allow having indices for multiple data streams in the error store (#99867) This fixes a bug where when trying to look up the unmanaged indices present in the DSL error store we'd remove all indices except the ones for one data stream (the first data stream we'd inspect). This now checks that we only remove the indices that were removed from the system, are not backing indices anymore, or are not managed by the _currently_ inspected data stream. Marking as non-issue as DSL was not released yet. --- .../lifecycle/DataStreamLifecycleService.java | 21 +++++-- .../DataStreamLifecycleServiceTests.java | 56 +++++++++++++++++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java index 3e2d5aae6db38..14de9193e01fe 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java @@ -42,6 +42,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.DataStream; import org.elasticsearch.cluster.metadata.DataStreamLifecycle; +import org.elasticsearch.cluster.metadata.IndexAbstraction; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.routing.allocation.AllocationService; @@ -672,13 +673,21 @@ private static List getTargetIndices( private void clearErrorStoreForUnmanagedIndices(DataStream dataStream) { Metadata metadata = clusterService.state().metadata(); for (String indexName : errorStore.getAllIndices()) { - IndexMetadata indexMeta = metadata.index(indexName); - if (indexMeta == null) { - logger.trace("Clearing recorded error for index [{}] because the index doesn't exist anymore", indexName); - errorStore.clearRecordedError(indexName); - } else if (dataStream.isIndexManagedByDataStreamLifecycle(indexMeta.getIndex(), metadata::index) == false) { - logger.trace("Clearing recorded error for index [{}] because the index is not managed by DSL anymore", indexName); + IndexAbstraction indexAbstraction = metadata.getIndicesLookup().get(indexName); + DataStream parentDataStream = indexAbstraction != null ? indexAbstraction.getParentDataStream() : null; + if (indexAbstraction == null || parentDataStream == null) { + logger.trace( + "Clearing recorded error for index [{}] because the index doesn't exist or is not a data stream backing index anymore", + indexName + ); errorStore.clearRecordedError(indexName); + } else if (parentDataStream.getName().equals(dataStream.getName())) { + // we're only verifying the indices that pertain to this data stream + IndexMetadata indexMeta = metadata.index(indexName); + if (dataStream.isIndexManagedByDataStreamLifecycle(indexMeta.getIndex(), metadata::index) == false) { + logger.trace("Clearing recorded error for index [{}] because the index is not managed by DSL anymore", indexName); + errorStore.clearRecordedError(indexName); + } } } } diff --git a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java index 712ad07bc0634..dd8f601a00f7f 100644 --- a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java +++ b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java @@ -367,6 +367,7 @@ public void testErrorStoreIsClearedOnBackingIndexBecomingUnmanaged() { metaBuilder.put(indexMetaBuilder.build(), true); } ClusterState updatedState = ClusterState.builder(state).metadata(metaBuilder).build(); + setState(clusterService, updatedState); dataStreamLifecycleService.run(updatedState); @@ -375,6 +376,61 @@ public void testErrorStoreIsClearedOnBackingIndexBecomingUnmanaged() { } } + public void testBackingIndicesFromMultipleDataStreamsInErrorStore() { + String ilmManagedDataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); + Metadata.Builder builder = Metadata.builder(); + DataStream ilmManagedDataStream = createDataStream( + builder, + ilmManagedDataStreamName, + 3, + settings(IndexVersion.current()), + DataStreamLifecycle.newBuilder().dataRetention(TimeValue.timeValueDays(700)).build(), + now + ); + // all backing indices are in the error store + for (Index index : ilmManagedDataStream.getIndices()) { + dataStreamLifecycleService.getErrorStore().recordError(index.getName(), new NullPointerException("will be ILM managed soon")); + } + String dataStreamWithBackingIndicesInErrorState = randomAlphaOfLength(15).toLowerCase(Locale.ROOT); + DataStream dslManagedDataStream = createDataStream( + builder, + dataStreamWithBackingIndicesInErrorState, + 5, + settings(IndexVersion.current()), + DataStreamLifecycle.newBuilder().dataRetention(TimeValue.timeValueDays(700)).build(), + now + ); + // put all backing indices in the error store + for (Index index : dslManagedDataStream.getIndices()) { + dataStreamLifecycleService.getErrorStore().recordError(index.getName(), new NullPointerException("dsl managed index")); + } + builder.put(ilmManagedDataStream); + builder.put(dslManagedDataStream); + ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(builder).build(); + + Metadata metadata = state.metadata(); + Metadata.Builder metaBuilder = Metadata.builder(metadata); + + // update the backing indices to be ILM managed so they should be removed from the error store on the next DSL run + for (Index index : ilmManagedDataStream.getIndices()) { + IndexMetadata indexMetadata = metadata.index(index); + IndexMetadata.Builder indexMetaBuilder = IndexMetadata.builder(indexMetadata); + indexMetaBuilder.settings(Settings.builder().put(indexMetadata.getSettings()).put(IndexMetadata.LIFECYCLE_NAME, "ILM_policy")); + metaBuilder.put(indexMetaBuilder.build(), true); + } + ClusterState updatedState = ClusterState.builder(state).metadata(metaBuilder).build(); + setState(clusterService, updatedState); + + dataStreamLifecycleService.run(updatedState); + + for (Index index : dslManagedDataStream.getIndices()) { + assertThat(dataStreamLifecycleService.getErrorStore().getError(index.getName()), notNullValue()); + } + for (Index index : ilmManagedDataStream.getIndices()) { + assertThat(dataStreamLifecycleService.getErrorStore().getError(index.getName()), nullValue()); + } + } + @SuppressWarnings("unchecked") public void testForceMerge() throws Exception { // We want this test method to get fake force merge responses, because this is what triggers a cluster state update From 24581188be70d006d045b33525a1fe20658c49f9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 25 Sep 2023 14:40:32 +0100 Subject: [PATCH 060/155] Make ChunkedRestResponseBody extend Releasable (#99871) If an unchunked body implements `Releasable` then it is released once the response is sent. We have some need for the same behaviour with chunked bodies, except that in this case there's no need for an `instanceof` check since we control the body type directly. This commit makes `ChunkedRestResponseBody extend Releasable` and adds support for closing it when the response is sent. --- .../Netty4HttpPipeliningHandlerTests.java | 3 + .../http/DefaultRestChannel.java | 7 +- .../rest/ChunkedRestResponseBody.java | 49 ++++++++++++- .../rest/LoggingChunkedRestResponseBody.java | 5 ++ .../org/elasticsearch/rest/RestResponse.java | 7 +- .../http/DefaultRestChannelTests.java | 28 +++++++- .../rest/ChunkedRestResponseBodyTests.java | 70 ++++++++++++------- 7 files changed, 139 insertions(+), 30 deletions(-) diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java index 1a011db433a49..895c07ec7a3f6 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java @@ -481,6 +481,9 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return "application/octet-stream"; } + + @Override + public void close() {} }; } diff --git a/server/src/main/java/org/elasticsearch/http/DefaultRestChannel.java b/server/src/main/java/org/elasticsearch/http/DefaultRestChannel.java index b2d3afe30cc36..930b20b927bd8 100644 --- a/server/src/main/java/org/elasticsearch/http/DefaultRestChannel.java +++ b/server/src/main/java/org/elasticsearch/http/DefaultRestChannel.java @@ -117,6 +117,7 @@ public void sendResponse(RestResponse restResponse) { final HttpResponse httpResponse; if (isHeadRequest == false && restResponse.isChunked()) { ChunkedRestResponseBody chunkedContent = restResponse.chunkedContent(); + toClose.add(chunkedContent); if (httpLogger != null && httpLogger.isBodyTracerEnabled()) { final var loggerStream = httpLogger.openResponseBodyLoggingStream(request.getRequestId()); toClose.add(() -> { @@ -132,8 +133,10 @@ public void sendResponse(RestResponse restResponse) { httpResponse = httpRequest.createResponse(restResponse.status(), chunkedContent); } else { final BytesReference content = restResponse.content(); - if (content instanceof Releasable) { - toClose.add((Releasable) content); + if (content instanceof Releasable releasable) { + toClose.add(releasable); + } else if (restResponse.isChunked()) { + toClose.add(restResponse.chunkedContent()); } toClose.add(this::releaseOutputBuffer); diff --git a/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java b/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java index 78e529eef2d98..fb73677e265f4 100644 --- a/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java +++ b/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java @@ -15,6 +15,8 @@ import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.IOUtils; +import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; import org.elasticsearch.core.RestApiVersion; import org.elasticsearch.core.Streams; @@ -32,7 +34,7 @@ * The body of a rest response that uses chunked HTTP encoding. Implementations are used to avoid materializing full responses on heap and * instead serialize only as much of the response as can be flushed to the network right away. */ -public interface ChunkedRestResponseBody { +public interface ChunkedRestResponseBody extends Releasable { /** * @return true once this response has been written fully. @@ -62,9 +64,29 @@ public interface ChunkedRestResponseBody { * @param params parameters to use for serialization * @param channel channel the response will be written to * @return chunked rest response body + * @deprecated Use {@link #fromXContent(ChunkedToXContent, ToXContent.Params, RestChannel, Releasable)} instead. */ + @Deprecated(forRemoval = true) static ChunkedRestResponseBody fromXContent(ChunkedToXContent chunkedToXContent, ToXContent.Params params, RestChannel channel) throws IOException { + return fromXContent(chunkedToXContent, params, channel, null); + } + + /** + * Create a chunked response body to be written to a specific {@link RestChannel} from a {@link ChunkedToXContent}. + * + * @param chunkedToXContent chunked x-content instance to serialize + * @param params parameters to use for serialization + * @param channel channel the response will be written to + * @param releasable resource to release when the response is fully sent, or {@code null} if nothing to release + * @return chunked rest response body + */ + static ChunkedRestResponseBody fromXContent( + ChunkedToXContent chunkedToXContent, + ToXContent.Params params, + RestChannel channel, + @Nullable Releasable releasable + ) throws IOException { return new ChunkedRestResponseBody() { @@ -132,14 +154,34 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return builder.getResponseContentTypeString(); } + + @Override + public void close() { + Releasables.closeExpectNoException(releasable); + } }; } /** * Create a chunked response body to be written to a specific {@link RestChannel} from a stream of text chunks, each represented as a * consumer of a {@link Writer}. The last chunk that the iterator yields must write at least one byte. + * + * @deprecated Use {@link #fromTextChunks(String, Iterator, Releasable)} instead. */ + @Deprecated(forRemoval = true) static ChunkedRestResponseBody fromTextChunks(String contentType, Iterator> chunkIterator) { + return fromTextChunks(contentType, chunkIterator, null); + } + + /** + * Create a chunked response body to be written to a specific {@link RestChannel} from a stream of text chunks, each represented as a + * consumer of a {@link Writer}. The last chunk that the iterator yields must write at least one byte. + */ + static ChunkedRestResponseBody fromTextChunks( + String contentType, + Iterator> chunkIterator, + @Nullable Releasable releasable + ) { return new ChunkedRestResponseBody() { private RecyclerBytesStreamOutput currentOutput; private final Writer writer = new OutputStreamWriter(new OutputStream() { @@ -209,6 +251,11 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return contentType; } + + @Override + public void close() { + Releasables.closeExpectNoException(releasable); + } }; } } diff --git a/server/src/main/java/org/elasticsearch/rest/LoggingChunkedRestResponseBody.java b/server/src/main/java/org/elasticsearch/rest/LoggingChunkedRestResponseBody.java index 0508828c70da1..00b56d0e05051 100644 --- a/server/src/main/java/org/elasticsearch/rest/LoggingChunkedRestResponseBody.java +++ b/server/src/main/java/org/elasticsearch/rest/LoggingChunkedRestResponseBody.java @@ -46,4 +46,9 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return inner.getResponseContentTypeString(); } + + @Override + public void close() { + inner.close(); + } } diff --git a/server/src/main/java/org/elasticsearch/rest/RestResponse.java b/server/src/main/java/org/elasticsearch/rest/RestResponse.java index 3a82a827e3726..1e86b7ddae367 100644 --- a/server/src/main/java/org/elasticsearch/rest/RestResponse.java +++ b/server/src/main/java/org/elasticsearch/rest/RestResponse.java @@ -16,6 +16,7 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.bytes.ReleasableBytesReference; import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.Nullable; import org.elasticsearch.xcontent.ToXContent; @@ -81,7 +82,11 @@ public RestResponse(RestStatus status, String responseMediaType, BytesReference public static RestResponse chunked(RestStatus restStatus, ChunkedRestResponseBody content) { if (content.isDone()) { - return new RestResponse(restStatus, content.getResponseContentTypeString(), BytesArray.EMPTY); + return new RestResponse( + restStatus, + content.getResponseContentTypeString(), + new ReleasableBytesReference(BytesArray.EMPTY, content) + ); } else { return new RestResponse(restStatus, content.getResponseContentTypeString(), null, content); } diff --git a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java index 6e0f58d0cdb97..63cdc2c485197 100644 --- a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java +++ b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java @@ -57,6 +57,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; @@ -525,6 +526,7 @@ public void testHandleHeadRequest() { } { // chunked response + final var isClosed = new AtomicBoolean(); channel.sendResponse(RestResponse.chunked(RestStatus.OK, new ChunkedRestResponseBody() { @Override @@ -541,11 +543,28 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return RestResponse.TEXT_CONTENT_TYPE; } + + @Override + public void close() { + assertTrue(isClosed.compareAndSet(false, true)); + } })); - verify(httpChannel, times(2)).sendResponse(requestCaptor.capture(), any()); + @SuppressWarnings("unchecked") + Class> listenerClass = (Class>) (Class) ActionListener.class; + ArgumentCaptor> listenerCaptor = ArgumentCaptor.forClass(listenerClass); + verify(httpChannel, times(2)).sendResponse(requestCaptor.capture(), listenerCaptor.capture()); HttpResponse response = requestCaptor.getValue(); assertThat(response, instanceOf(TestHttpResponse.class)); assertThat(((TestHttpResponse) response).content().length(), equalTo(0)); + + ActionListener listener = listenerCaptor.getValue(); + assertFalse(isClosed.get()); + if (randomBoolean()) { + listener.onResponse(null); + } else { + listener.onFailure(new ClosedChannelException()); + } + assertTrue(isClosed.get()); } } @@ -703,6 +722,7 @@ public HttpResponse createResponse(RestStatus status, ChunkedRestResponseBody co ) ); + final var isClosed = new AtomicBoolean(); assertEquals( responseBody, ChunkedLoggingStreamTests.getDecodedLoggedBody( @@ -730,10 +750,16 @@ public ReleasableBytesReference encodeChunk(int sizeHint, Recycler rec public String getResponseContentTypeString() { return RestResponse.TEXT_CONTENT_TYPE; } + + @Override + public void close() { + assertTrue(isClosed.compareAndSet(false, true)); + } })) ) ); + assertTrue(isClosed.get()); } private TestHttpResponse executeRequest(final Settings settings, final String host) { diff --git a/server/src/test/java/org/elasticsearch/rest/ChunkedRestResponseBodyTests.java b/server/src/test/java/org/elasticsearch/rest/ChunkedRestResponseBodyTests.java index 9842aff24dac1..485e2a3a3fdd7 100644 --- a/server/src/test/java/org/elasticsearch/rest/ChunkedRestResponseBodyTests.java +++ b/server/src/test/java/org/elasticsearch/rest/ChunkedRestResponseBodyTests.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; public class ChunkedRestResponseBodyTests extends ESTestCase { @@ -50,40 +51,59 @@ public void testEncodesChunkedXContentCorrectly() throws IOException { } final var bytesDirect = BytesReference.bytes(builderDirect); - final var chunkedResponse = ChunkedRestResponseBody.fromXContent( - chunkedToXContent, - ToXContent.EMPTY_PARAMS, - new FakeRestChannel( - new FakeRestRequest.Builder(xContentRegistry()).withContent(BytesArray.EMPTY, randomXContent.type()).build(), - randomBoolean(), - 1 + final var isClosed = new AtomicBoolean(); + try ( + var chunkedResponse = ChunkedRestResponseBody.fromXContent( + chunkedToXContent, + ToXContent.EMPTY_PARAMS, + new FakeRestChannel( + new FakeRestRequest.Builder(xContentRegistry()).withContent(BytesArray.EMPTY, randomXContent.type()).build(), + randomBoolean(), + 1 + ), + () -> assertTrue(isClosed.compareAndSet(false, true)) ) - ); + ) { - final List refsGenerated = new ArrayList<>(); - while (chunkedResponse.isDone() == false) { - refsGenerated.add(chunkedResponse.encodeChunk(randomIntBetween(2, 10), BytesRefRecycler.NON_RECYCLING_INSTANCE)); - } + final List refsGenerated = new ArrayList<>(); + while (chunkedResponse.isDone() == false) { + refsGenerated.add(chunkedResponse.encodeChunk(randomIntBetween(2, 10), BytesRefRecycler.NON_RECYCLING_INSTANCE)); + } - assertEquals(bytesDirect, CompositeBytesReference.of(refsGenerated.toArray(new BytesReference[0]))); + assertEquals(bytesDirect, CompositeBytesReference.of(refsGenerated.toArray(new BytesReference[0]))); + assertFalse(isClosed.get()); + } + assertTrue(isClosed.get()); } public void testFromTextChunks() throws IOException { final var chunks = randomList(1000, () -> randomUnicodeOfLengthBetween(1, 100)); - final var body = ChunkedRestResponseBody.fromTextChunks("text/plain", Iterators.map(chunks.iterator(), s -> w -> w.write(s))); - - final List refsGenerated = new ArrayList<>(); - while (body.isDone() == false) { - refsGenerated.add(body.encodeChunk(randomIntBetween(2, 10), BytesRefRecycler.NON_RECYCLING_INSTANCE)); - } - final BytesReference chunkedBytes = CompositeBytesReference.of(refsGenerated.toArray(new BytesReference[0])); + final var isClosed = new AtomicBoolean(); + try ( + var body = ChunkedRestResponseBody.fromTextChunks( + "text/plain", + Iterators.map(chunks.iterator(), s -> w -> w.write(s)), + () -> assertTrue(isClosed.compareAndSet(false, true)) + ) + ) { + final List refsGenerated = new ArrayList<>(); + while (body.isDone() == false) { + refsGenerated.add(body.encodeChunk(randomIntBetween(2, 10), BytesRefRecycler.NON_RECYCLING_INSTANCE)); + } + final BytesReference chunkedBytes = CompositeBytesReference.of(refsGenerated.toArray(new BytesReference[0])); - try (var outputStream = new ByteArrayOutputStream(); var writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) { - for (final var chunk : chunks) { - writer.write(chunk); + try ( + var outputStream = new ByteArrayOutputStream(); + var writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8) + ) { + for (final var chunk : chunks) { + writer.write(chunk); + } + writer.flush(); + assertEquals(new BytesArray(outputStream.toByteArray()), chunkedBytes); } - writer.flush(); - assertEquals(new BytesArray(outputStream.toByteArray()), chunkedBytes); + assertFalse(isClosed.get()); } + assertTrue(isClosed.get()); } } From 6b7c4e297fd9b0bfedce4dc60559737a4cf9bad0 Mon Sep 17 00:00:00 2001 From: gheorghepucea <91881042+gheorghepucea@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:37:07 +0200 Subject: [PATCH 061/155] Updating the documentation for contributing functions in esql. (#99844) * Updating the documentation for function contributions in esql. * Removed unecessary parameter from docs command. --- .../esql/expression/function/scalar/package-info.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/package-info.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/package-info.java index 2387b2571c710..b03d9bdc409c4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/package-info.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/package-info.java @@ -40,7 +40,7 @@ *
  • * Run the csv tests (see {@code x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java}) * from within Intellij or, alternatively, via Gradle: - * {@code ./gradlew -p x-pack/plugin/esql test --tests "org.elasticsearch.xpack.esql.CsvTests"} + * {@code ./gradlew :x-pack:plugin:esql:test --tests "org.elasticsearch.xpack.esql.CsvTests"} * IntelliJ will take a few minutes to compile everything but the test itself should take only a few seconds. * This is a fast path to running ESQL's integration tests. *
  • @@ -63,7 +63,8 @@ * that contain the actual inner implementation of the function. Modify those to look right * and click {@code Build->Recompile 'FunctionName.java'} in IntelliJ. This should generate * an {@link org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator} implementation - * calling the method annotated with {@link org.elasticsearch.compute.ann.Evaluator}. + * calling the method annotated with {@link org.elasticsearch.compute.ann.Evaluator}. Please commit the + * generated evaluator before submitting your PR. *
  • * Once your evaluator is generated you can implement * {@link org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper#toEvaluator}, @@ -129,13 +130,14 @@ * {@code docs/reference/esql/functions/signature/myfunction.svg } * and here * {@code docs/reference/esql/functions/types/myfunction.asciidoc} - * Make sure to commit them and reference them in your doc file. + * Make sure to commit them and reference them in your doc file. There are plenty of examples on how + * to reference those files e.g. {@code docs/reference/esql/functions/sin.asciidoc}. *
  • *
  • * Build the docs by cloning the docs repo * and running: *
    {@code
    - * ../docs/build_docs --doc docs/reference/index.asciidoc --resource x-pack/docs/ --open --chunk 1
    + * ../docs/build_docs --doc docs/reference/index.asciidoc --open --chunk 1
      *          }
    * from the elasticsearch directory. The first time you run the docs build it does a bunch * of things with docker to get itself ready. Hopefully you can sit back and watch the show. From eec9acfba5fe51e03c432ecc79df50fdfdb6daf2 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 25 Sep 2023 11:46:01 -0400 Subject: [PATCH 062/155] ESQL: Close pages on response (#99876) This modifies ESQL to release all `Block`s when we reply to across the wire with their contents. We'd like to make all blocks `Releasable` but to do that we're really going to have to, well, release them all. This starts "at the back" of the ESQL request process. --- .../org/elasticsearch/compute/data/Page.java | 20 ++++ .../operator/exchange/ExchangeResponse.java | 41 ++++++- .../xpack/esql/action/EsqlActionIT.java | 106 +++++++++--------- .../esql/action/EsqlActionRuntimeFieldIT.java | 10 +- .../xpack/esql/action/EsqlQueryResponse.java | 9 +- .../esql/action/EsqlResponseListener.java | 41 ++++--- 6 files changed, 153 insertions(+), 74 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java index 42998770e2d84..b5ea1aa6284ce 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.core.Assertions; +import org.elasticsearch.core.Releasables; import java.io.IOException; import java.util.Arrays; @@ -33,6 +34,14 @@ public final class Page implements Writeable { private final int positionCount; + /** + * True if we've called {@link #releaseBlocks()} which causes us to remove the + * circuit breaker for the {@link Block}s. The {@link Page} reference should be + * removed shortly after this and reading {@linkplain Block}s after release + * will fail. + */ + private boolean blocksReleased = false; + /** * Creates a new page with the given blocks. Every block has the same number of positions. * @@ -93,6 +102,9 @@ private static int determinePositionCount(Block... blocks) { * @return the block */ public B getBlock(int blockIndex) { + if (blocksReleased) { + throw new IllegalStateException("can't read released page"); + } @SuppressWarnings("unchecked") B block = (B) blocks[blockIndex]; return block; @@ -201,6 +213,14 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Release all blocks in this page, decrementing any breakers accounting for these blocks. + */ + public void releaseBlocks() { + blocksReleased = true; + Releasables.closeExpectNoException(blocks); + } + public static class PageWriter implements Writeable.Writer { @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java index ec7a66d213d5f..ee5ac7f91c96e 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java @@ -10,15 +10,26 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.AbstractRefCounted; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.RefCounted; +import org.elasticsearch.core.Releasable; import org.elasticsearch.transport.TransportResponse; import java.io.IOException; import java.util.Objects; -public final class ExchangeResponse extends TransportResponse { +public final class ExchangeResponse extends TransportResponse implements Releasable { + private final RefCounted counted = AbstractRefCounted.of(this::close); private final Page page; private final boolean finished; + /** + * We always use the remote exchange framwork even for local exchanges, but + * local exchanges shouldn't close the Page. Remote exchanges totally should + * as soon as they've serialized the block. This is a clever hack Nhat mentioned + * that can do that. But I don't like it. But it works and might unstick us. + */ + private boolean serialized = false; public ExchangeResponse(Page page, boolean finished) { this.page = page; @@ -33,6 +44,7 @@ public ExchangeResponse(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { + serialized = true; out.writeOptionalWriteable(page); out.writeBoolean(finished); } @@ -65,4 +77,31 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(page, finished); } + + @Override + public void incRef() { + counted.incRef(); + } + + @Override + public boolean tryIncRef() { + return counted.tryIncRef(); + } + + @Override + public boolean decRef() { + return counted.decRef(); + } + + @Override + public boolean hasReferences() { + return counted.hasReferences(); + } + + @Override + public void close() { + if (serialized && page != null) { + page.releaseBlocks(); + } + } } diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java index a001ece05725a..f8aeee1569f2e 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java @@ -492,12 +492,13 @@ public void testFilterWithNullAndEval() { } public void testStringLength() { - EsqlQueryResponse results = run("from test | eval l = length(color)"); - logger.info(results); - assertThat(getValuesList(results), hasSize(40)); - int countIndex = results.columns().indexOf(new ColumnInfo("l", "integer")); - for (List values : getValuesList(results)) { - assertThat((Integer) values.get(countIndex), greaterThanOrEqualTo(3)); + try (EsqlQueryResponse results = run("from test | eval l = length(color)")) { + logger.info(results); + assertThat(getValuesList(results), hasSize(40)); + int countIndex = results.columns().indexOf(new ColumnInfo("l", "integer")); + for (List values : getValuesList(results)) { + assertThat((Integer) values.get(countIndex), greaterThanOrEqualTo(3)); + } } } @@ -742,19 +743,20 @@ record Doc(long val, String tag) { indexRandom(true, randomBoolean(), indexRequests); int limit = randomIntBetween(1, 10); String command = "from test_extract_fields | sort val | limit " + limit; - EsqlQueryResponse results = run(command); - logger.info(results); - // _doc, _segment, _shard are pruned - assertThat(results.columns().size(), equalTo(2)); - assertThat(getValuesList(results), hasSize(Math.min(limit, numDocs))); - assertThat(results.columns().get(1).name(), equalTo("val")); - assertThat(results.columns().get(0).name(), equalTo("tag")); - List actualDocs = new ArrayList<>(); - for (int i = 0; i < getValuesList(results).size(); i++) { - List values = getValuesList(results).get(i); - actualDocs.add(new Doc((Long) values.get(1), (String) values.get(0))); + try (EsqlQueryResponse results = run(command)) { + logger.info(results); + // _doc, _segment, _shard are pruned + assertThat(results.columns().size(), equalTo(2)); + assertThat(getValuesList(results), hasSize(Math.min(limit, numDocs))); + assertThat(results.columns().get(1).name(), equalTo("val")); + assertThat(results.columns().get(0).name(), equalTo("tag")); + List actualDocs = new ArrayList<>(); + for (int i = 0; i < getValuesList(results).size(); i++) { + List values = getValuesList(results).get(i); + actualDocs.add(new Doc((Long) values.get(1), (String) values.get(0))); + } + assertThat(actualDocs, equalTo(allDocs.stream().limit(limit).toList())); } - assertThat(actualDocs, equalTo(allDocs.stream().limit(limit).toList())); } public void testEvalWithNullAndAvg() { @@ -941,49 +943,47 @@ public void testTopNPushedToLucene() { } client().admin().indices().prepareRefresh("test").get(); - EsqlQueryResponse results = run(""" + try (EsqlQueryResponse results = run(""" from test | where color == "yellow" | sort data desc nulls first, count asc nulls first | limit 10 | keep data, count, color - """); - logger.info(results); - Assert.assertEquals(3, results.columns().size()); - Assert.assertEquals(10, getValuesList(results).size()); + """)) { + logger.info(results); + Assert.assertEquals(3, results.columns().size()); + Assert.assertEquals(10, getValuesList(results).size()); - // assert column metadata - assertEquals("data", results.columns().get(0).name()); - assertEquals("long", results.columns().get(0).type()); - assertEquals("count", results.columns().get(1).name()); - assertEquals("long", results.columns().get(1).type()); - assertEquals("color", results.columns().get(2).name()); - assertEquals("keyword", results.columns().get(2).type()); - record Group(Long data, Long count, String color) { - Group(Long data, Long count) { - this(data, count, "yellow"); + // assert column metadata + assertEquals("data", results.columns().get(0).name()); + assertEquals("long", results.columns().get(0).type()); + assertEquals("count", results.columns().get(1).name()); + assertEquals("long", results.columns().get(1).type()); + assertEquals("color", results.columns().get(2).name()); + assertEquals("keyword", results.columns().get(2).type()); + record Group(Long data, Long count, String color) { + Group(Long data, Long count) { + this(data, count, "yellow"); + } } + List expectedGroups = List.of( + // data sorted descending nulls first; count sorted ascending nulls first + new Group(null, 50L), + new Group(null, 60L), + new Group(null, 70L), + new Group(null, 80L), + new Group(null, 90L), + new Group(null, 100L), + new Group(10L, null), + new Group(10L, 100L), + new Group(9L, null), + new Group(9L, 90L) + ); + List actualGroups = getValuesList(results).stream() + .map(l -> new Group((Long) l.get(0), (Long) l.get(1), (String) l.get(2))) + .toList(); + assertThat(actualGroups, equalTo(expectedGroups)); } - List expectedGroups = List.of( - // data sorted descending nulls first; count sorted ascending nulls first - new Group(null, 50L), - new Group(null, 60L), - new Group(null, 70L), - new Group(null, 80L), - new Group(null, 90L), - new Group(null, 100L), - new Group(10L, null), - new Group(10L, 100L), - new Group(9L, null), - new Group(9L, 90L) - ); - List actualGroups = getValuesList(results).stream() - .map(l -> new Group((Long) l.get(0), (Long) l.get(1), (String) l.get(2))) - .toList(); - assertThat(actualGroups, equalTo(expectedGroups)); - - // clean-up what we created - bulkDelete.get(); } /** diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java index 76c874b0fe63d..4a21cc5a77521 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java @@ -65,8 +65,9 @@ public void testDouble() throws InterruptedException, IOException { public void testKeyword() throws InterruptedException, IOException { createIndexWithConstRuntimeField("keyword"); - EsqlQueryResponse response = run("from test | keep const | limit 1"); - assertThat(getValuesList(response), equalTo(List.of(List.of("const")))); + try (EsqlQueryResponse response = run("from test | keep const | limit 1")) { + assertThat(getValuesList(response), equalTo(List.of(List.of("const")))); + } } /** @@ -81,8 +82,9 @@ public void testKeywordBy() throws InterruptedException, IOException { public void testBoolean() throws InterruptedException, IOException { createIndexWithConstRuntimeField("boolean"); - EsqlQueryResponse response = run("from test | sort foo | limit 3"); - assertThat(getValuesList(response), equalTo(List.of(List.of(true, 0L), List.of(true, 1L), List.of(true, 2L)))); + try (EsqlQueryResponse response = run("from test | sort foo | limit 3")) { + assertThat(getValuesList(response), equalTo(List.of(List.of(true, 0L), List.of(true, 1L), List.of(true, 2L)))); + } } public void testDate() throws InterruptedException, IOException { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java index 2b029a03fa9f9..796f009cfd3bb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java @@ -23,6 +23,8 @@ import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.lucene.UnsupportedValueSource; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.xcontent.InstantiatingObjectParser; import org.elasticsearch.xcontent.ObjectParser; @@ -48,7 +50,7 @@ import static org.elasticsearch.xpack.ql.util.NumericUtils.unsignedLongAsNumber; import static org.elasticsearch.xpack.ql.util.StringUtils.parseIP; -public class EsqlQueryResponse extends ActionResponse implements ChunkedToXContent { +public class EsqlQueryResponse extends ActionResponse implements ChunkedToXContent, Releasable { private final List columns; private final List pages; @@ -190,6 +192,11 @@ public String toString() { return Strings.toString(ChunkedToXContent.wrapAsToXContent(this)); } + @Override + public void close() { + Releasables.close(() -> Iterators.map(pages.iterator(), p -> p::releaseBlocks)); + } + public static Iterator> pagesToValues(List dataTypes, List pages) { BytesRef scratch = new BytesRef(); return Iterators.flatMap( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java index 5ccaeec436f70..4dd0a3ec77419 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java @@ -118,22 +118,33 @@ public EsqlResponseListener(RestChannel channel, RestRequest restRequest, EsqlQu @Override public RestResponse buildResponse(EsqlQueryResponse esqlResponse) throws Exception { - RestResponse restResponse; - if (mediaType instanceof TextFormat format) { - restResponse = RestResponse.chunked( - RestStatus.OK, - ChunkedRestResponseBody.fromTextChunks(format.contentType(restRequest), format.format(restRequest, esqlResponse)) - ); - } else { - restResponse = RestResponse.chunked( - RestStatus.OK, - ChunkedRestResponseBody.fromXContent(esqlResponse, channel.request(), channel) - ); + boolean success = false; + try { + RestResponse restResponse; + if (mediaType instanceof TextFormat format) { + restResponse = RestResponse.chunked( + RestStatus.OK, + ChunkedRestResponseBody.fromTextChunks( + format.contentType(restRequest), + format.format(restRequest, esqlResponse), + esqlResponse + ) + ); + } else { + restResponse = RestResponse.chunked( + RestStatus.OK, + ChunkedRestResponseBody.fromXContent(esqlResponse, channel.request(), channel, esqlResponse) + ); + } + long tookNanos = stopWatch.stop().getNanos(); + restResponse.addHeader(HEADER_NAME_TOOK_NANOS, Long.toString(tookNanos)); + success = true; + return restResponse; + } finally { + if (success == false) { + esqlResponse.close(); + } } - long tookNanos = stopWatch.stop().getNanos(); - restResponse.addHeader(HEADER_NAME_TOOK_NANOS, Long.toString(tookNanos)); - - return restResponse; } /** From a1caba1521703573b258cbefbaeaeb3911978b8e Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 25 Sep 2023 17:17:03 +0100 Subject: [PATCH 063/155] Add repository backup warning (#99788) Adds a note about the consequences of trying to back up and restore a snapshot repository without taking steps to make sure the copy is consistent. --- .../snapshot-restore/register-repository.asciidoc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/reference/snapshot-restore/register-repository.asciidoc b/docs/reference/snapshot-restore/register-repository.asciidoc index f8000f0dfed5e..28b0640a8fae5 100644 --- a/docs/reference/snapshot-restore/register-repository.asciidoc +++ b/docs/reference/snapshot-restore/register-repository.asciidoc @@ -258,7 +258,13 @@ of the underlying filesystem and then take a backup of this filesystem snapshot. It is very important that the filesystem snapshot is taken atomically. -WARNING: You cannot use filesystem snapshots of individual nodes as a backup +WARNING: Do not rely on repository backups that were taken by methods other +than the one described in this section. If you use another method to make a +copy of your repository contents then the resulting copy may capture an +inconsistent view of your data. Restoring a repository from such a copy may +fail, reporting errors, or may succeed having silently lost some of your data. + +WARNING: Do not use filesystem snapshots of individual nodes as a backup mechanism. You must use the {es} snapshot and restore feature to copy the cluster contents to a separate repository. Then, if desired, you can take a filesystem snapshot of this repository. From dfec8361c447e1e7e6844dc44d80f78e46057110 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Mon, 25 Sep 2023 19:13:29 +0200 Subject: [PATCH 064/155] ESQL: Lower the implicit limit, if none is user-provided (#99816) This lowers the implicit (max) 10K limit to 500 (default). The new default limit is only added if no other limit is detected after the last pipeline breaker. Otherwise the capping max limit is applied. --- docs/changelog/99816.yaml | 6 ++ .../xpack/esql/EsqlTestUtils.java | 21 ++++--- .../xpack/esql/analysis/Analyzer.java | 10 ++-- .../xpack/esql/plugin/EsqlPlugin.java | 8 +++ .../esql/plugin/TransportEsqlQueryAction.java | 3 +- .../xpack/esql/session/EsqlConfiguration.java | 14 ++++- .../elasticsearch/xpack/esql/CsvTests.java | 12 +--- .../xpack/esql/analysis/AnalyzerTests.java | 55 ++++++++++++++++++- .../optimizer/PhysicalPlanOptimizerTests.java | 15 +---- .../xpack/esql/planner/EvalMapperTests.java | 10 +++- .../planner/LocalExecutionPlannerTests.java | 3 +- .../esql/plugin/DataNodeRequestTests.java | 14 +---- .../EsqlConfigurationSerializationTests.java | 8 ++- 13 files changed, 122 insertions(+), 57 deletions(-) create mode 100644 docs/changelog/99816.yaml diff --git a/docs/changelog/99816.yaml b/docs/changelog/99816.yaml new file mode 100644 index 0000000000000..4caf8a36f54b4 --- /dev/null +++ b/docs/changelog/99816.yaml @@ -0,0 +1,6 @@ +pr: 99816 +summary: "ESQL: Lower the implicit limit, if none is user-provided" +area: ES|QL +type: enhancement +issues: + - 99458 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java index dd86742785d15..3176f433f043a 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java @@ -81,17 +81,22 @@ public byte[] max(String field, DataType dataType) { public static final TestSearchStats TEST_SEARCH_STATS = new TestSearchStats(); - public static final EsqlConfiguration TEST_CFG = new EsqlConfiguration( - DateUtils.UTC, - Locale.US, - null, - null, - new QueryPragmas(Settings.EMPTY), - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY) - ); + public static final EsqlConfiguration TEST_CFG = configuration(new QueryPragmas(Settings.EMPTY)); private EsqlTestUtils() {} + public static EsqlConfiguration configuration(QueryPragmas pragmas) { + return new EsqlConfiguration( + DateUtils.UTC, + Locale.US, + null, + null, + pragmas, + EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY), + EsqlPlugin.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY) + ); + } + public static Literal L(Object value) { return of(value); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java index e511874afa486..8732321e8d068 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java @@ -613,11 +613,11 @@ private static LogicalPlan removeAggDuplicates(Aggregate agg) { private static class AddImplicitLimit extends ParameterizedRule { @Override public LogicalPlan apply(LogicalPlan logicalPlan, AnalyzerContext context) { - return new Limit( - Source.EMPTY, - new Literal(Source.EMPTY, context.configuration().resultTruncationMaxSize(), DataTypes.INTEGER), - logicalPlan - ); + List limits = logicalPlan.collectFirstChildren(Limit.class::isInstance); + var limit = limits.isEmpty() == false + ? context.configuration().resultTruncationMaxSize() // user provided a limit: cap result entries to the max + : context.configuration().resultTruncationDefaultSize(); // user provided no limit: cap to a default + return new Limit(Source.EMPTY, new Literal(Source.EMPTY, limit, DataTypes.INTEGER), logicalPlan); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java index 783f5550a00af..b4f91ca1ceb11 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java @@ -76,6 +76,14 @@ public class EsqlPlugin extends Plugin implements ActionPlugin { Setting.Property.NodeScope ); + public static final Setting QUERY_RESULT_TRUNCATION_DEFAULT_SIZE = Setting.intSetting( + "esql.query.result_truncation_max_size", + 500, + 1, + 10000, + Setting.Property.NodeScope + ); + @Override public Collection createComponents( Client client, diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java index 550e42e715228..6cddf5b67af9c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java @@ -96,7 +96,8 @@ private void doExecuteForked(Task task, EsqlQueryRequest request, ActionListener null, clusterService.getClusterName().value(), request.pragmas(), - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.get(settings) + EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.get(settings), + EsqlPlugin.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.get(settings) ); String sessionId = sessionID(task); planExecutor.esql( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlConfiguration.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlConfiguration.java index 3d6f5ce18816f..0a00515d79eb4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlConfiguration.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlConfiguration.java @@ -23,6 +23,7 @@ public class EsqlConfiguration extends Configuration implements Writeable { private final QueryPragmas pragmas; private final int resultTruncationMaxSize; + private final int resultTruncationDefaultSize; private final Locale locale; @@ -32,12 +33,14 @@ public EsqlConfiguration( String username, String clusterName, QueryPragmas pragmas, - int resultTruncationMaxSize + int resultTruncationMaxSize, + int resultTruncationDefaultSize ) { super(zi, username, clusterName); this.locale = locale; this.pragmas = pragmas; this.resultTruncationMaxSize = resultTruncationMaxSize; + this.resultTruncationDefaultSize = resultTruncationDefaultSize; } public EsqlConfiguration(StreamInput in) throws IOException { @@ -45,6 +48,7 @@ public EsqlConfiguration(StreamInput in) throws IOException { locale = Locale.forLanguageTag(in.readString()); this.pragmas = new QueryPragmas(in); this.resultTruncationMaxSize = in.readVInt(); + this.resultTruncationDefaultSize = in.readVInt(); } @Override @@ -58,6 +62,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(locale.toLanguageTag()); pragmas.writeTo(out); out.writeVInt(resultTruncationMaxSize); + out.writeVInt(resultTruncationDefaultSize); } public QueryPragmas pragmas() { @@ -68,6 +73,10 @@ public int resultTruncationMaxSize() { return resultTruncationMaxSize; } + public int resultTruncationDefaultSize() { + return resultTruncationDefaultSize; + } + public Locale locale() { return locale; } @@ -77,6 +86,7 @@ public boolean equals(Object o) { if (super.equals(o)) { EsqlConfiguration that = (EsqlConfiguration) o; return resultTruncationMaxSize == that.resultTruncationMaxSize + && resultTruncationDefaultSize == that.resultTruncationDefaultSize && Objects.equals(pragmas, that.pragmas) && Objects.equals(locale, that.locale); } @@ -85,6 +95,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), pragmas, resultTruncationMaxSize, locale); + return Objects.hash(super.hashCode(), pragmas, resultTruncationMaxSize, resultTruncationDefaultSize, locale); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index ab78640030fb1..dfe1d6165a8e8 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -67,7 +67,6 @@ import org.elasticsearch.xpack.esql.planner.Mapper; import org.elasticsearch.xpack.esql.planner.PlannerUtils; import org.elasticsearch.xpack.esql.planner.TestPhysicalOperationProviders; -import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.esql.plugin.QueryPragmas; import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.esql.stats.DisabledSearchStats; @@ -87,12 +86,10 @@ import java.io.IOException; import java.net.URL; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -151,13 +148,8 @@ public class CsvTests extends ESTestCase { private final Integer lineNumber; private final CsvSpecReader.CsvTestCase testCase; - private final EsqlConfiguration configuration = new EsqlConfiguration( - ZoneOffset.UTC, - Locale.US, - null, - null, - new QueryPragmas(Settings.builder().put("page_size", randomPageSize()).build()), - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY) + private final EsqlConfiguration configuration = EsqlTestUtils.configuration( + new QueryPragmas(Settings.builder().put("page_size", randomPageSize()).build()) ); private final FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); private final EsqlParser parser = new EsqlParser(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java index 06050e41f73de..1ee90256b95dd 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java @@ -7,11 +7,13 @@ package org.elasticsearch.xpack.esql.analysis; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.esql.expression.function.aggregate.Max; import org.elasticsearch.xpack.esql.plan.logical.EsqlUnresolvedRelation; import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Row; +import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.Expressions; @@ -25,6 +27,7 @@ import org.elasticsearch.xpack.ql.plan.TableIdentifier; import org.elasticsearch.xpack.ql.plan.logical.Aggregate; import org.elasticsearch.xpack.ql.plan.logical.EsRelation; +import org.elasticsearch.xpack.ql.plan.logical.Filter; import org.elasticsearch.xpack.ql.plan.logical.Limit; import org.elasticsearch.xpack.ql.plan.logical.OrderBy; import org.elasticsearch.xpack.ql.type.DataType; @@ -42,6 +45,7 @@ import static org.elasticsearch.xpack.ql.tree.Source.EMPTY; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; @@ -54,6 +58,9 @@ public class AnalyzerTests extends ESTestCase { List.of() ); + private static final int MAX_LIMIT = EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY); + private static final int DEFAULT_LIMIT = EsqlPlugin.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY); + public void testIndexResolution() { EsIndex idx = new EsIndex("idx", Map.of()); Analyzer analyzer = analyzer(IndexResolution.valid(idx)); @@ -821,14 +828,60 @@ public void testProjectAggGroupsRefs() { """, "d", "last_name"); } - public void testExplicitProjectAndLimit() { + public void testImplicitLimit() { var plan = analyze(""" from test """); var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(DEFAULT_LIMIT)); as(limit.child(), EsRelation.class); } + public void testImplicitMaxLimitAfterLimit() { + for (int i = -1; i <= 1; i++) { + var plan = analyze("from test | limit " + (MAX_LIMIT + i)); + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(MAX_LIMIT)); + limit = as(limit.child(), Limit.class); + as(limit.child(), EsRelation.class); + } + } + + /* + Limit[10000[INTEGER]] + \_Filter[s{r}#3 > 0[INTEGER]] + \_Eval[[salary{f}#10 * 10[INTEGER] AS s]] + \_Limit[10000[INTEGER]] + \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..] + */ + public void testImplicitMaxLimitAfterLimitAndNonLimit() { + for (int i = -1; i <= 1; i++) { + var plan = analyze("from test | limit " + (MAX_LIMIT + i) + " | eval s = salary * 10 | where s > 0"); + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(MAX_LIMIT)); + var filter = as(limit.child(), Filter.class); + var eval = as(filter.child(), Eval.class); + limit = as(eval.child(), Limit.class); + as(limit.child(), EsRelation.class); + } + } + + public void testImplicitDefaultLimitAfterLimitAndBreaker() { + for (var breaker : List.of("stats c = count(salary) by last_name", "sort salary")) { + var plan = analyze("from test | limit 100000 | " + breaker); + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(MAX_LIMIT)); + } + } + + public void testImplicitDefaultLimitAfterBreakerAndNonBreakers() { + for (var breaker : List.of("stats c = count(salary) by last_name", "eval c = salary | sort c")) { + var plan = analyze("from test | " + breaker + " | eval cc = c * 10 | where cc > 0"); + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(DEFAULT_LIMIT)); + } + } + private static final String[] COMPARISONS = new String[] { "==", "!=", "<", "<=", ">", ">=" }; public void testCompareIntToString() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java index 79add5bc08e6b..0bb43539dba72 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java @@ -57,7 +57,6 @@ import org.elasticsearch.xpack.esql.planner.Mapper; import org.elasticsearch.xpack.esql.planner.PhysicalVerificationException; import org.elasticsearch.xpack.esql.planner.PlannerUtils; -import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.esql.plugin.QueryPragmas; import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery; import org.elasticsearch.xpack.esql.session.EsqlConfiguration; @@ -76,13 +75,11 @@ import org.elasticsearch.xpack.ql.index.EsIndex; import org.elasticsearch.xpack.ql.index.IndexResolution; import org.elasticsearch.xpack.ql.type.DataTypes; -import org.elasticsearch.xpack.ql.type.DateUtils; import org.elasticsearch.xpack.ql.type.EsField; import org.junit.Before; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -90,6 +87,7 @@ import static java.util.Arrays.asList; import static org.elasticsearch.core.Tuple.tuple; import static org.elasticsearch.xpack.esql.EsqlTestUtils.as; +import static org.elasticsearch.xpack.esql.EsqlTestUtils.configuration; import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping; import static org.elasticsearch.xpack.esql.EsqlTestUtils.statsForMissingField; import static org.elasticsearch.xpack.esql.SerializationTestUtils.assertSerialization; @@ -129,16 +127,7 @@ public class PhysicalPlanOptimizerTests extends ESTestCase { public static List readScriptSpec() { return settings().stream().map(t -> { var settings = Settings.builder().loadFromMap(t.v2()).build(); - return new Object[] { - t.v1(), - new EsqlConfiguration( - DateUtils.UTC, - Locale.US, - null, - null, - new QueryPragmas(settings), - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(settings) - ) }; + return new Object[] { t.v1(), configuration(new QueryPragmas(settings)) }; }).toList(); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java index aa13838b28266..f0d4f0534caee 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java @@ -65,7 +65,15 @@ public class EvalMapperTests extends ESTestCase { private static final FieldAttribute LONG = field("long", DataTypes.LONG); private static final FieldAttribute DATE = field("date", DataTypes.DATETIME); - private static final EsqlConfiguration TEST_CONFIG = new EsqlConfiguration(ZoneOffset.UTC, Locale.US, "test", null, null, 10000000); + private static final EsqlConfiguration TEST_CONFIG = new EsqlConfiguration( + ZoneOffset.UTC, + Locale.US, + "test", + null, + null, + 10000000, + 10000 + ); @ParametersFactory(argumentFormatting = "%1$s") public static List params() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java index 645833f01ba28..95ef6e7baf70c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java @@ -135,7 +135,8 @@ private EsqlConfiguration config() { "test_user", "test_cluser", pragmas, - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(null) + EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(null), + EsqlPlugin.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(null) ); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java index fae2a1caeab4c..0e08c693952fc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java @@ -27,7 +27,6 @@ import org.elasticsearch.xpack.esql.parser.EsqlParser; import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.esql.planner.Mapper; -import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.esql.session.EsqlConfigurationSerializationTests; import org.elasticsearch.xpack.esql.stats.Metrics; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; @@ -37,11 +36,10 @@ import org.elasticsearch.xpack.ql.type.EsField; import java.io.IOException; -import java.time.ZoneOffset; import java.util.List; -import java.util.Locale; import java.util.Map; +import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_CFG; import static org.elasticsearch.xpack.esql.EsqlTestUtils.emptyPolicyResolution; import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping; @@ -176,15 +174,7 @@ static LogicalPlan parse(String query) { } static PhysicalPlan mapAndMaybeOptimize(LogicalPlan logicalPlan) { - var configuration = new EsqlConfiguration( - ZoneOffset.UTC, - Locale.US, - null, - null, - new QueryPragmas(Settings.EMPTY), - EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY) - ); - var physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(configuration)); + var physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(TEST_CFG)); FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); var mapper = new Mapper(functionRegistry); var physical = mapper.map(logicalPlan); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlConfigurationSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlConfigurationSerializationTests.java index bc1146c492a73..777e035f6492a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlConfigurationSerializationTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlConfigurationSerializationTests.java @@ -34,8 +34,9 @@ public static EsqlConfiguration randomConfiguration() { var username = randomAlphaOfLengthBetween(1, 10); var clusterName = randomAlphaOfLengthBetween(3, 10); var truncation = randomNonNegativeInt(); + var defaultTruncation = randomNonNegativeInt(); - return new EsqlConfiguration(zoneId, locale, username, clusterName, randomQueryPragmas(), truncation); + return new EsqlConfiguration(zoneId, locale, username, clusterName, randomQueryPragmas(), truncation, defaultTruncation); } @Override @@ -45,7 +46,7 @@ protected EsqlConfiguration createTestInstance() { @Override protected EsqlConfiguration mutateInstance(EsqlConfiguration in) throws IOException { - int ordinal = between(0, 5); + int ordinal = between(0, 6); return new EsqlConfiguration( ordinal == 0 ? randomValueOtherThan(in.zoneId(), () -> randomZone().normalized()) : in.zoneId(), ordinal == 1 ? randomValueOtherThan(in.locale(), () -> randomLocale(random())) : in.locale(), @@ -54,7 +55,8 @@ protected EsqlConfiguration mutateInstance(EsqlConfiguration in) throws IOExcept ordinal == 4 ? new QueryPragmas(Settings.builder().put(QueryPragmas.EXCHANGE_BUFFER_SIZE.getKey(), between(1, 10)).build()) : in.pragmas(), - ordinal == 5 ? in.resultTruncationMaxSize() + randomIntBetween(3, 10) : in.resultTruncationMaxSize() + ordinal == 5 ? in.resultTruncationMaxSize() + randomIntBetween(3, 10) : in.resultTruncationMaxSize(), + ordinal == 6 ? in.resultTruncationDefaultSize() + randomIntBetween(3, 10) : in.resultTruncationDefaultSize() ); } } From 5597c1da839f0eab5d4f5b525f592705010eea52 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 25 Sep 2023 18:35:34 +0100 Subject: [PATCH 065/155] Inline & remove ChunkedRestResponseBody overrides (#99875) Follow-up to #99871 to inline and remove the deprecated overrides which do not take a `Releasable` parameter. --- .../Netty4HttpServerTransportTests.java | 2 +- .../rest/ChunkedRestResponseBody.java | 26 ------------------- .../action/RestChunkedToXContentListener.java | 2 +- .../cluster/RestNodesHotThreadsAction.java | 2 +- .../rest/action/cat/RestTable.java | 6 +++-- .../action/info/RestClusterInfoAction.java | 3 ++- .../elasticsearch/rest/RestResponseTests.java | 2 +- 7 files changed, 10 insertions(+), 33 deletions(-) diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java index b0a42470bdb22..c08d571eaf6bb 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java @@ -614,7 +614,7 @@ public void dispatchRequest(final RestRequest request, final RestChannel channel channel.sendResponse( RestResponse.chunked(OK, ChunkedRestResponseBody.fromXContent(ignored -> Iterators.single((builder, params) -> { throw new AssertionError("should not be called for HEAD REQUEST"); - }), ToXContent.EMPTY_PARAMS, channel)) + }), ToXContent.EMPTY_PARAMS, channel, null)) ); } catch (IOException e) { throw new AssertionError(e); diff --git a/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java b/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java index fb73677e265f4..9cfe7b84577db 100644 --- a/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java +++ b/server/src/main/java/org/elasticsearch/rest/ChunkedRestResponseBody.java @@ -57,21 +57,6 @@ public interface ChunkedRestResponseBody extends Releasable { */ String getResponseContentTypeString(); - /** - * Create a chunked response body to be written to a specific {@link RestChannel} from a {@link ChunkedToXContent}. - * - * @param chunkedToXContent chunked x-content instance to serialize - * @param params parameters to use for serialization - * @param channel channel the response will be written to - * @return chunked rest response body - * @deprecated Use {@link #fromXContent(ChunkedToXContent, ToXContent.Params, RestChannel, Releasable)} instead. - */ - @Deprecated(forRemoval = true) - static ChunkedRestResponseBody fromXContent(ChunkedToXContent chunkedToXContent, ToXContent.Params params, RestChannel channel) - throws IOException { - return fromXContent(chunkedToXContent, params, channel, null); - } - /** * Create a chunked response body to be written to a specific {@link RestChannel} from a {@link ChunkedToXContent}. * @@ -162,17 +147,6 @@ public void close() { }; } - /** - * Create a chunked response body to be written to a specific {@link RestChannel} from a stream of text chunks, each represented as a - * consumer of a {@link Writer}. The last chunk that the iterator yields must write at least one byte. - * - * @deprecated Use {@link #fromTextChunks(String, Iterator, Releasable)} instead. - */ - @Deprecated(forRemoval = true) - static ChunkedRestResponseBody fromTextChunks(String contentType, Iterator> chunkIterator) { - return fromTextChunks(contentType, chunkIterator, null); - } - /** * Create a chunked response body to be written to a specific {@link RestChannel} from a stream of text chunks, each represented as a * consumer of a {@link Writer}. The last chunk that the iterator yields must write at least one byte. diff --git a/server/src/main/java/org/elasticsearch/rest/action/RestChunkedToXContentListener.java b/server/src/main/java/org/elasticsearch/rest/action/RestChunkedToXContentListener.java index 77e30883ef702..a3bb1ed9d94dc 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/RestChunkedToXContentListener.java +++ b/server/src/main/java/org/elasticsearch/rest/action/RestChunkedToXContentListener.java @@ -37,7 +37,7 @@ public RestChunkedToXContentListener(RestChannel channel, ToXContent.Params para @Override protected void processResponse(Response response) throws IOException { channel.sendResponse( - RestResponse.chunked(getRestStatus(response), ChunkedRestResponseBody.fromXContent(response, params, channel)) + RestResponse.chunked(getRestStatus(response), ChunkedRestResponseBody.fromXContent(response, params, channel, null)) ); } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java index 77d0b0e9a4f3e..095abcd14d355 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java @@ -115,7 +115,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC return channel -> client.admin().cluster().nodesHotThreads(nodesHotThreadsRequest, new RestResponseListener<>(channel) { @Override public RestResponse buildResponse(NodesHotThreadsResponse response) { - return RestResponse.chunked(RestStatus.OK, fromTextChunks(TEXT_CONTENT_TYPE, response.getTextChunks())); + return RestResponse.chunked(RestStatus.OK, fromTextChunks(TEXT_CONTENT_TYPE, response.getTextChunks(), null)); } }); } diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java index 5c9887a8e0916..8ce001f7a1a77 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestTable.java @@ -77,7 +77,8 @@ public static RestResponse buildXContentBuilder(Table table, RestChannel channel Iterators.single((builder, params) -> builder.endArray()) ), ToXContent.EMPTY_PARAMS, - channel + channel, + null ) ); } @@ -126,7 +127,8 @@ public static RestResponse buildTextPlainResponse(Table table, RestChannel chann } writer.append("\n"); }) - ) + ), + null ) ); } diff --git a/server/src/main/java/org/elasticsearch/rest/action/info/RestClusterInfoAction.java b/server/src/main/java/org/elasticsearch/rest/action/info/RestClusterInfoAction.java index 60ae860ef6ec0..7bcc19cb17fa9 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/info/RestClusterInfoAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/info/RestClusterInfoAction.java @@ -129,7 +129,8 @@ public RestResponse buildResponse(NodesStatsResponse response) throws Exception ChunkedToXContentHelper.endObject() ), EMPTY_PARAMS, - channel + channel, + null ) ); } diff --git a/server/src/test/java/org/elasticsearch/rest/RestResponseTests.java b/server/src/test/java/org/elasticsearch/rest/RestResponseTests.java index 67602be3a83ea..14b5fe7f49482 100644 --- a/server/src/test/java/org/elasticsearch/rest/RestResponseTests.java +++ b/server/src/test/java/org/elasticsearch/rest/RestResponseTests.java @@ -69,7 +69,7 @@ public void testWithHeaders() throws Exception { public void testEmptyChunkedBody() { RestResponse response = RestResponse.chunked( RestStatus.OK, - ChunkedRestResponseBody.fromTextChunks(RestResponse.TEXT_CONTENT_TYPE, Collections.emptyIterator()) + ChunkedRestResponseBody.fromTextChunks(RestResponse.TEXT_CONTENT_TYPE, Collections.emptyIterator(), null) ); assertFalse(response.isChunked()); assertNotNull(response.content()); From 8a0542564ae9f67c85e30adc19ad167583f7a9d3 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Mon, 25 Sep 2023 11:35:33 -0700 Subject: [PATCH 066/155] Preserve response headers in ESQL CSV tests (#99883) This change is to retrieve response headers within the responding thread since we can't pass them between threads in a blocking fashion. --- .../java/org/elasticsearch/xpack/esql/CsvTests.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index dfe1d6165a8e8..0f1d9257a6c0a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -384,11 +384,12 @@ protected void start(Driver driver, ActionListener driverListener) { Driver.start(threadPool.executor(ESQL_THREAD_POOL_NAME), driver, between(1, 1000), driverListener); } }; - PlainActionFuture future = new PlainActionFuture<>(); - runner.runToCompletion(drivers, ActionListener.releaseAfter(future, () -> Releasables.close(drivers))); - future.actionGet(TimeValue.timeValueSeconds(30)); - var responseHeaders = threadPool.getThreadContext().getResponseHeaders(); - return new ActualResults(columnNames, columnTypes, dataTypes, collectedPages, responseHeaders); + PlainActionFuture future = new PlainActionFuture<>(); + runner.runToCompletion(drivers, ActionListener.releaseAfter(future, () -> Releasables.close(drivers)).map(ignore -> { + var responseHeaders = threadPool.getThreadContext().getResponseHeaders(); + return new ActualResults(columnNames, columnTypes, dataTypes, collectedPages, responseHeaders); + })); + return future.actionGet(TimeValue.timeValueSeconds(30)); } finally { Releasables.close(() -> Releasables.close(drivers), exchangeSource::decRef); assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); From 0fa3a41398cbf7d12acf38b6c6f2e3bc7e0c987d Mon Sep 17 00:00:00 2001 From: William Brafford Date: Mon, 25 Sep 2023 22:18:32 -0400 Subject: [PATCH 067/155] Use Build.current().minCompat methods instead of Version when building strings (#99834) * Use Build.current() methods instead of Version when building strings --- .../java/org/elasticsearch/env/NodeEnvironmentIT.java | 5 +++-- .../java/org/elasticsearch/env/NodeEnvironment.java | 9 +++++---- .../main/java/org/elasticsearch/env/NodeMetadata.java | 5 +++-- .../org/elasticsearch/env/NodeEnvironmentTests.java | 3 ++- .../java/org/elasticsearch/env/NodeMetadataTests.java | 5 +++-- .../org/elasticsearch/test/rest/ESRestTestCase.java | 11 +++++++---- .../rest/VersionSensitiveWarningsHandlerTests.java | 8 ++++---- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java index a8971838b3023..63c3297a4e5de 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java @@ -8,6 +8,7 @@ package org.elasticsearch.env; +import org.elasticsearch.Build; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNodeRole; @@ -140,9 +141,9 @@ public void testFailsToStartIfUpgradedTooFar() { startsWith("cannot upgrade a node from version ["), endsWith( "] directly to version [" - + Version.CURRENT + + Build.current().version() + "], upgrade to version [" - + Version.CURRENT.minimumCompatibilityVersion() + + Build.current().minWireCompatVersion() + "] first." ) ) diff --git a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java index 0f4de31f1e541..1b40f3740b5b3 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -20,6 +20,7 @@ import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.NIOFSDirectory; import org.apache.lucene.store.NativeFSLockFactory; +import org.elasticsearch.Build; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetadata; @@ -512,9 +513,9 @@ static void checkForIndexCompatibility(Logger logger, DataPath... dataPaths) thr if (metadata == null) { throw new CorruptStateException( "Format version is not supported. Upgrading to [" - + Version.CURRENT + + Build.current().version() + "] is only supported from version [" - + Version.CURRENT.minimumCompatibilityVersion() + + Build.current().minWireCompatVersion() + "]." ); } @@ -528,13 +529,13 @@ static void checkForIndexCompatibility(Logger logger, DataPath... dataPaths) thr "Cannot start this node because it holds metadata for indices with version [" + metadata.oldestIndexVersion() + "] with which this node of version [" - + Version.CURRENT + + Build.current().version() + "] is incompatible. Revert this node to version [" + Version.max(Version.CURRENT.minimumCompatibilityVersion(), metadata.previousNodeVersion()) + "] and delete any indices with versions earlier than [" + IndexVersion.MINIMUM_COMPATIBLE + "] before upgrading to version [" - + Version.CURRENT + + Build.current().version() + "]. If all such indices have already been deleted, revert this node to version [" + Version.max(Version.CURRENT.minimumCompatibilityVersion(), metadata.previousNodeVersion()) + "] and wait for it to join the cluster to clean up any older indices from its metadata." diff --git a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java index 1de3539bf93ed..fba5a0c96be3c 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java +++ b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java @@ -8,6 +8,7 @@ package org.elasticsearch.env; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.gateway.MetadataStateFormat; import org.elasticsearch.index.IndexVersion; @@ -117,10 +118,10 @@ public void verifyUpgradeToCurrentVersion() { "cannot upgrade a node from version [" + nodeVersion + "] directly to version [" - + Version.CURRENT + + Build.current().version() + "], " + "upgrade to version [" - + Version.CURRENT.minimumCompatibilityVersion() + + Build.current().minWireCompatVersion() + "] first." ); } diff --git a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java index 14d9090f83fad..f6c2655d74023 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java @@ -16,6 +16,7 @@ import org.apache.lucene.store.NIOFSDirectory; import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.util.SetOnce; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -619,7 +620,7 @@ public void testIndexCompatibilityChecks() throws IOException { ); assertThat(ex.getMessage(), startsWith("cannot upgrade a node from version [" + oldVersion + "] directly")); - assertThat(ex.getMessage(), containsString("upgrade to version [" + Version.CURRENT.minimumCompatibilityVersion())); + assertThat(ex.getMessage(), containsString("upgrade to version [" + Build.current().minWireCompatVersion())); } } diff --git a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java index 7698f70555a73..309fcf78fe544 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java @@ -7,6 +7,7 @@ */ package org.elasticsearch.env; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.core.Tuple; import org.elasticsearch.gateway.MetadataStateFormat; @@ -137,9 +138,9 @@ public void testDoesNotUpgradeAncientVersion() { startsWith("cannot upgrade a node from version ["), endsWith( "] directly to version [" - + Version.CURRENT + + Build.current().version() + "], upgrade to version [" - + Version.CURRENT.minimumCompatibilityVersion() + + Build.current().minWireCompatVersion() + "] first." ) ) diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index 3041e918bc5a6..21a78bfd38aad 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -20,6 +20,7 @@ import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; @@ -311,9 +312,9 @@ protected String getTestReadinessPorts() { public static class VersionSensitiveWarningsHandler implements WarningsHandler { Set requiredSameVersionClusterWarnings = new HashSet<>(); Set allowedWarnings = new HashSet<>(); - final Set testNodeVersions; + final Set testNodeVersions; - public VersionSensitiveWarningsHandler(Set nodeVersions) { + public VersionSensitiveWarningsHandler(Set nodeVersions) { this.testNodeVersions = nodeVersions; } @@ -355,14 +356,16 @@ public boolean warningsShouldFailRequest(List warnings) { private boolean isExclusivelyTargetingCurrentVersionCluster() { assertFalse("Node versions running in the cluster are missing", testNodeVersions.isEmpty()); - return testNodeVersions.size() == 1 && testNodeVersions.iterator().next().equals(Version.CURRENT); + return testNodeVersions.size() == 1 && testNodeVersions.iterator().next().equals(Build.current().version()); } } public static RequestOptions expectVersionSpecificWarnings(Consumer expectationsSetter) { Builder builder = RequestOptions.DEFAULT.toBuilder(); - VersionSensitiveWarningsHandler warningsHandler = new VersionSensitiveWarningsHandler(nodeVersions); + VersionSensitiveWarningsHandler warningsHandler = new VersionSensitiveWarningsHandler( + nodeVersions.stream().map(Version::toString).collect(Collectors.toSet()) + ); expectationsSetter.accept(warningsHandler); builder.setWarningsHandler(warningsHandler); return builder.build(); diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/VersionSensitiveWarningsHandlerTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/VersionSensitiveWarningsHandlerTests.java index 85c83ccfd30f7..f489b2bf78fe3 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/VersionSensitiveWarningsHandlerTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/VersionSensitiveWarningsHandlerTests.java @@ -8,7 +8,7 @@ package org.elasticsearch.test.rest; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.client.WarningsHandler; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.ESRestTestCase.VersionSensitiveWarningsHandler; @@ -21,7 +21,7 @@ public class VersionSensitiveWarningsHandlerTests extends ESTestCase { public void testSameVersionCluster() throws IOException { - WarningsHandler handler = expectVersionSpecificWarnings(Set.of(Version.CURRENT), v -> v.current("expectedCurrent1")); + WarningsHandler handler = expectVersionSpecificWarnings(Set.of(Build.current().version()), v -> v.current("expectedCurrent1")); assertFalse(handler.warningsShouldFailRequest(List.of("expectedCurrent1"))); assertTrue(handler.warningsShouldFailRequest(List.of("expectedCurrent1", "unexpected"))); assertTrue(handler.warningsShouldFailRequest(List.of())); @@ -30,7 +30,7 @@ public void testSameVersionCluster() throws IOException { public void testMixedVersionCluster() throws IOException { WarningsHandler handler = expectVersionSpecificWarnings( - Set.of(Version.CURRENT, Version.CURRENT.minimumCompatibilityVersion()), + Set.of(Build.current().version(), Build.current().minWireCompatVersion()), v -> { v.current("expectedCurrent1"); v.compatible("Expected legacy warning"); @@ -45,7 +45,7 @@ public void testMixedVersionCluster() throws IOException { } private static WarningsHandler expectVersionSpecificWarnings( - Set nodeVersions, + Set nodeVersions, Consumer expectationsSetter ) { // Based on EsRestTestCase.expectVersionSpecificWarnings helper method but without ESRestTestCase dependency From 602aa013ab8777a75db08a0bbd1939d4e5d8b9a8 Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Tue, 26 Sep 2023 06:54:41 +0200 Subject: [PATCH 068/155] [Profiling] Tighten resource creation check (#99873) Index management in the Universal Profiling plugin manages different types of resources: 1. Component templates, composable templates and ILM policies 2. Indices and data streams The latter require the former to be present so they can be created properly. This is achieved by checking that the predicate `isAllResourcesCreated` in `ProfilingIndexTemplateRegistry` returns `true`. While this predicate checks that all its managed resources are present, it does not check whether their version is correct. This can lead to a race condition where an older version of e.g. an index template is present and an index that matches this index template is rolled over but the new index still uses the old index template. With this commit we tighten the check so that the predicate returns `true` if and only if all its managed resources are present *and* the version is not outdated. This avoids that race condition as the creation of indices and data streams will only proceed after all preconditions are met. --- docs/changelog/99873.yaml | 5 ++ .../ProfilingIndexTemplateRegistry.java | 28 +++++-- .../ProfilingIndexTemplateRegistryTests.java | 75 +++++++++++++++++++ 3 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 docs/changelog/99873.yaml diff --git a/docs/changelog/99873.yaml b/docs/changelog/99873.yaml new file mode 100644 index 0000000000000..d726ba00a1558 --- /dev/null +++ b/docs/changelog/99873.yaml @@ -0,0 +1,5 @@ +pr: 99873 +summary: "[Profiling] Tighten resource creation check" +area: Application +type: bug +issues: [] diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java index ad1ed60b1504e..1d19210c31692 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java @@ -282,7 +282,7 @@ protected boolean isUpgradeRequired(LifecyclePolicy currentPolicy, LifecyclePoli } } - private int getVersion(LifecyclePolicy policy, String logicalVersion) { + private static int getVersion(LifecyclePolicy policy, String logicalVersion) { Map meta = policy.getMetadata(); try { return meta != null ? Integer.parseInt(meta.getOrDefault("version", Integer.MIN_VALUE).toString()) : Integer.MIN_VALUE; @@ -294,21 +294,35 @@ private int getVersion(LifecyclePolicy policy, String logicalVersion) { } } + /** + * Determines whether all resources (component templates, composable templates and lifecycle policies) have been created. This method + * also checks whether resources have been created for the expected version. + * + * @param state Current cluster state. + * @param settings Current cluster settings. + * @return true if and only if all resources managed by this registry have been created and are current. + */ public static boolean isAllResourcesCreated(ClusterState state, Settings settings) { - for (String componentTemplate : COMPONENT_TEMPLATE_CONFIGS.keySet()) { - if (state.metadata().componentTemplates().containsKey(componentTemplate) == false) { + for (String name : COMPONENT_TEMPLATE_CONFIGS.keySet()) { + ComponentTemplate componentTemplate = state.metadata().componentTemplates().get(name); + if (componentTemplate == null || componentTemplate.version() < INDEX_TEMPLATE_VERSION) { return false; } } - for (String composableTemplate : COMPOSABLE_INDEX_TEMPLATE_CONFIGS.keySet()) { - if (state.metadata().templatesV2().containsKey(composableTemplate) == false) { + for (String name : COMPOSABLE_INDEX_TEMPLATE_CONFIGS.keySet()) { + ComposableIndexTemplate composableIndexTemplate = state.metadata().templatesV2().get(name); + if (composableIndexTemplate == null || composableIndexTemplate.version() < INDEX_TEMPLATE_VERSION) { return false; } } if (isDataStreamsLifecycleOnlyMode(settings) == false) { + IndexLifecycleMetadata ilmMetadata = state.metadata().custom(IndexLifecycleMetadata.TYPE); + if (ilmMetadata == null) { + return false; + } for (LifecyclePolicyConfig lifecyclePolicy : LIFECYCLE_POLICY_CONFIGS) { - IndexLifecycleMetadata ilmMetadata = state.metadata().custom(IndexLifecycleMetadata.TYPE); - if (ilmMetadata == null || ilmMetadata.getPolicies().containsKey(lifecyclePolicy.getPolicyName()) == false) { + LifecyclePolicy existingPolicy = ilmMetadata.getPolicies().get(lifecyclePolicy.getPolicyName()); + if (existingPolicy == null || getVersion(existingPolicy, "current") < INDEX_TEMPLATE_VERSION) { return false; } } diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistryTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistryTests.java index a596d36b54821..a5a2ee7d89768 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistryTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistryTests.java @@ -309,6 +309,81 @@ public void testPolicyUpgraded() throws Exception { } } + public void testAllResourcesPresentButOutdated() { + DiscoveryNode node = DiscoveryNodeUtils.create("node"); + DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); + Map componentTemplates = new HashMap<>(); + Map composableTemplates = new HashMap<>(); + Map policies = new HashMap<>(); + for (String templateName : registry.getComponentTemplateConfigs().keySet()) { + // outdated (or missing) version + componentTemplates.put(templateName, frequently() ? ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION - 1 : null); + } + for (String templateName : registry.getComposableTemplateConfigs().keySet()) { + // outdated (or missing) version + composableTemplates.put(templateName, frequently() ? ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION - 1 : null); + } + for (LifecyclePolicy policy : registry.getLifecyclePolicies()) { + // make a copy as we're modifying the version mapping + Map metadata = new HashMap<>(policy.getMetadata()); + if (frequently()) { + metadata.put("version", ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION - 1); + } else { + metadata.remove("version"); + } + policies.put(policy.getName(), new LifecyclePolicy(policy.getName(), policy.getPhases(), metadata)); + } + ClusterState clusterState = createClusterState(Settings.EMPTY, componentTemplates, composableTemplates, policies, nodes); + + assertFalse(ProfilingIndexTemplateRegistry.isAllResourcesCreated(clusterState, Settings.EMPTY)); + } + + public void testAllResourcesPresentAndCurrent() { + DiscoveryNode node = DiscoveryNodeUtils.create("node"); + DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); + Map componentTemplates = new HashMap<>(); + Map composableTemplates = new HashMap<>(); + Map policies = new HashMap<>(); + for (String templateName : registry.getComponentTemplateConfigs().keySet()) { + componentTemplates.put(templateName, ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION); + } + for (String templateName : registry.getComposableTemplateConfigs().keySet()) { + composableTemplates.put(templateName, ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION); + } + for (LifecyclePolicy policy : registry.getLifecyclePolicies()) { + policies.put(policy.getName(), policy); + } + ClusterState clusterState = createClusterState(Settings.EMPTY, componentTemplates, composableTemplates, policies, nodes); + + assertTrue(ProfilingIndexTemplateRegistry.isAllResourcesCreated(clusterState, Settings.EMPTY)); + } + + public void testSomeResourcesMissing() { + DiscoveryNode node = DiscoveryNodeUtils.create("node"); + DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); + Map componentTemplates = new HashMap<>(); + Map composableTemplates = new HashMap<>(); + Map policies = new HashMap<>(); + for (String templateName : registry.getComponentTemplateConfigs().keySet()) { + if (rarely()) { + componentTemplates.put(templateName, ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION); + } + } + for (String templateName : registry.getComposableTemplateConfigs().keySet()) { + if (rarely()) { + composableTemplates.put(templateName, ProfilingIndexTemplateRegistry.INDEX_TEMPLATE_VERSION); + } + } + for (LifecyclePolicy policy : registry.getLifecyclePolicies()) { + if (rarely()) { + policies.put(policy.getName(), policy); + } + } + ClusterState clusterState = createClusterState(Settings.EMPTY, componentTemplates, composableTemplates, policies, nodes); + + assertFalse(ProfilingIndexTemplateRegistry.isAllResourcesCreated(clusterState, Settings.EMPTY)); + } + private ActionResponse verifyComposableTemplateInstalled( AtomicInteger calledTimes, ActionType action, From 40029a77d1f9306a9caefdd870feedf16c035695 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 26 Sep 2023 09:03:53 +0100 Subject: [PATCH 069/155] [ML] Extend use of MlConfigVersion to one more place (#99878) In #97699 we moved away from Version to MlConfigVersion for versioning ML configurations. However, one place was missed. This PR adjusts that missing place, namely the metadata on DFA destination indices. As a result of this change being shipped in a later version than the original change, a hack is required to map the intervening product versions to the MlConfigVersion that was in force. --- .../xpack/core/ml/MlConfigVersion.java | 9 +++- .../action/StartDataFrameAnalyticsAction.java | 3 +- ...ransportStartDataFrameAnalyticsAction.java | 13 ------ .../xpack/ml/dataframe/DestinationIndex.java | 16 +++---- ...ortStartDataFrameAnalyticsActionTests.java | 42 ------------------- .../ml/dataframe/DestinationIndexTests.java | 7 ++-- 6 files changed, 20 insertions(+), 70 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java index 0e3f60d56843d..37446a5551fd2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java @@ -321,13 +321,18 @@ public static MlConfigVersion getMlConfigVersionForNode(DiscoveryNode node) { } // Parse an MlConfigVersion from a string. - // Note that version "8.10.0" is silently converted to "10.0.0". + // Note that version "8.10.x" and "8.11.0" are silently converted to "10.0.0". // This is to support upgrade scenarios in pre-prod QA environments. public static MlConfigVersion fromString(String str) { if (str == null) { return CURRENT; } - if (str.equals("8.10.0")) { + // The whole switch from Version to MlConfigVersion was supposed to take + // place during development of 8.10.0, however, one place was missed. As + // a result there may be DFA destination indices in the wild with metadata + // containing 8.10.1, 8.10.2, 8.10.3 or 8.11.0. We can treat these as V_10 + // for config version comparison purposes. + if (str.startsWith("8.10.") || str.equals("8.11.0")) { return V_10; } Matcher matcher = Pattern.compile("^(\\d+)\\.0\\.0$").matcher(str); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StartDataFrameAnalyticsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StartDataFrameAnalyticsAction.java index dd56eec10200b..85a7202817e83 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StartDataFrameAnalyticsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StartDataFrameAnalyticsAction.java @@ -8,7 +8,6 @@ import org.elasticsearch.TransportVersion; import org.elasticsearch.TransportVersions; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.master.MasterNodeRequest; @@ -148,7 +147,7 @@ public static class TaskParams implements PersistentTaskParams, MlTaskParams { public static final MlConfigVersion VERSION_INTRODUCED = MlConfigVersion.V_7_3_0; public static final TransportVersion TRANSPORT_VERSION_INTRODUCED = TransportVersions.V_7_3_0; - public static final Version VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED = Version.V_7_10_0; + public static final MlConfigVersion VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED = MlConfigVersion.V_7_10_0; public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( MlTasks.DATA_FRAME_ANALYTICS_TASK_NAME, diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java index e24ffbff06f98..d8f1bbf9389c9 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java @@ -810,19 +810,6 @@ public static String nodeFilter(DiscoveryNode node, TaskParams params) { + TaskParams.VERSION_INTRODUCED + "] or higher"; } - if (node.getVersion().before(TaskParams.VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED) - && params.getVersion().onOrAfter(MlConfigVersion.fromVersion(TaskParams.VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED))) { - return "Not opening job [" - + id - + "] on node [" - + JobNodeSelector.nodeNameAndVersion(node) - + "], because the data frame analytics created for version [" - + params.getVersion() - + "] requires a node of version " - + "[" - + TaskParams.VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED - + "] or higher"; - } return null; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndex.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndex.java index 6073029fcf0b9..4798e699f46c4 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndex.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndex.java @@ -8,7 +8,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; @@ -33,6 +32,7 @@ import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.xpack.core.ClientHelper; +import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.action.StartDataFrameAnalyticsAction; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsDest; @@ -95,7 +95,7 @@ public final class DestinationIndex { * If the results mappings change in a way existing destination indices will fail to index * the results, this should be bumped accordingly. */ - public static final Version MIN_COMPATIBLE_VERSION = + public static final MlConfigVersion MIN_COMPATIBLE_VERSION = StartDataFrameAnalyticsAction.TaskParams.VERSION_DESTINATION_INDEX_MAPPINGS_CHANGED; private DestinationIndex() {} @@ -202,7 +202,7 @@ private static CreateIndexRequest createIndexRequest( checkResultsFieldIsNotPresentInProperties(config, properties); properties.putAll(createAdditionalMappings(config, fieldCapabilitiesResponse)); Map metadata = getOrPutDefault(mappingsAsMap, META, HashMap::new); - metadata.putAll(createMetadata(config.getId(), clock, Version.CURRENT)); + metadata.putAll(createMetadata(config.getId(), clock, MlConfigVersion.CURRENT)); if (config.getSource().getRuntimeMappings().isEmpty() == false) { Map runtimeMappings = getOrPutDefault(mappingsAsMap, RUNTIME, HashMap::new); runtimeMappings.putAll(config.getSource().getRuntimeMappings()); @@ -317,7 +317,7 @@ private static Map createAdditionalMappings( } // Visible for testing - static Map createMetadata(String analyticsId, Clock clock, Version version) { + static Map createMetadata(String analyticsId, Clock clock, MlConfigVersion version) { Map metadata = new HashMap<>(); metadata.put(CREATION_DATE_MILLIS, clock.millis()); metadata.put(CREATED_BY, DFA_CREATOR); @@ -403,11 +403,11 @@ public static Metadata readMetadata(String jobId, MappingMetadata mappingMetadat } @SuppressWarnings("unchecked") - private static Version getVersion(String jobId, Map meta) { + private static MlConfigVersion getVersion(String jobId, Map meta) { try { Map version = (Map) meta.get(VERSION); String createdVersionString = (String) version.get(CREATED); - return Version.fromString(createdVersionString); + return MlConfigVersion.fromString(createdVersionString); } catch (Exception e) { logger.error(() -> "[" + jobId + "] Could not retrieve destination index version", e); return null; @@ -443,9 +443,9 @@ public String getVersion() { private static class DestMetadata implements Metadata { - private final Version version; + private final MlConfigVersion version; - private DestMetadata(Version version) { + private DestMetadata(MlConfigVersion version) { this.version = version; } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsActionTests.java index 1a677a1df69bb..b3a8751051061 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsActionTests.java @@ -103,48 +103,6 @@ public void testGetAssignment_NoMlNodes() { ); } - // Cannot assign the node because none of the existing nodes is appropriate: - // - _node_name0 is too old (version 7.2.0) - // - _node_name1 is too old (version 7.9.1) - // - _node_name2 is too old (version 7.9.2) - public void testGetAssignment_MlNodesAreTooOld() { - TaskExecutor executor = createTaskExecutor(); - TaskParams params = new TaskParams(JOB_ID, MlConfigVersion.CURRENT, false); - ClusterState clusterState = ClusterState.builder(new ClusterName("_name")) - .metadata(Metadata.builder().putCustom(MlMetadata.TYPE, new MlMetadata.Builder().build())) - .nodes( - DiscoveryNodes.builder() - .add(createNode(0, true, Version.V_7_2_0, MlConfigVersion.V_7_2_0)) - .add(createNode(1, true, Version.V_7_9_1, MlConfigVersion.V_7_9_1)) - .add(createNode(2, true, Version.V_7_9_2, MlConfigVersion.V_7_9_2)) - ) - .build(); - - Assignment assignment = executor.getAssignment(params, clusterState.nodes().getAllNodes(), clusterState); - assertThat(assignment.getExecutorNode(), is(nullValue())); - assertThat( - assignment.getExplanation(), - allOf( - containsString( - "Not opening job [data_frame_id] on node [{_node_name0}{version=7.2.0}], " - + "because the data frame analytics requires a node of version [7.3.0] or higher" - ), - containsString( - "Not opening job [data_frame_id] on node [{_node_name1}{version=7.9.1}], " - + "because the data frame analytics created for version [" - + MlConfigVersion.CURRENT - + "] requires a node of version [7.10.0] or higher" - ), - containsString( - "Not opening job [data_frame_id] on node [{_node_name2}{version=7.9.2}], " - + "because the data frame analytics created for version [" - + MlConfigVersion.CURRENT - + "] requires a node of version [7.10.0] or higher" - ) - ) - ); - } - // The node can be assigned despite being newer than the job. // In such a case destination index will be created from scratch so that its mappings are up-to-date. public void testGetAssignment_MlNodeIsNewerThanTheMlJobButTheAssignmentSuceeds() { diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndexTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndexTests.java index 822b27400c419..5c928ff6e8a3a 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndexTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/DestinationIndexTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.json.JsonXContent; +import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsDest; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsSource; @@ -748,7 +749,7 @@ public void testReadMetadata_GivenMetaNotCreatedByAnalytics() { public void testReadMetadata_GivenCurrentVersion() { Map mappings = new HashMap<>(); - mappings.put("_meta", DestinationIndex.createMetadata("test_id", Clock.systemUTC(), Version.CURRENT)); + mappings.put("_meta", DestinationIndex.createMetadata("test_id", Clock.systemUTC(), MlConfigVersion.CURRENT)); MappingMetadata mappingMetadata = mock(MappingMetadata.class); when(mappingMetadata.getSourceAsMap()).thenReturn(mappings); @@ -756,7 +757,7 @@ public void testReadMetadata_GivenCurrentVersion() { assertThat(metadata.hasMetadata(), is(true)); assertThat(metadata.isCompatible(), is(true)); - assertThat(metadata.getVersion(), equalTo(Version.CURRENT.toString())); + assertThat(metadata.getVersion(), equalTo(MlConfigVersion.CURRENT.toString())); } public void testReadMetadata_GivenMinCompatibleVersion() { @@ -774,7 +775,7 @@ public void testReadMetadata_GivenMinCompatibleVersion() { public void testReadMetadata_GivenIncompatibleVersion() { Map mappings = new HashMap<>(); - mappings.put("_meta", DestinationIndex.createMetadata("test_id", Clock.systemUTC(), Version.V_7_9_3)); + mappings.put("_meta", DestinationIndex.createMetadata("test_id", Clock.systemUTC(), MlConfigVersion.V_7_9_3)); MappingMetadata mappingMetadata = mock(MappingMetadata.class); when(mappingMetadata.getSourceAsMap()).thenReturn(mappings); From eaf21483fb47ac2e8605ac2b128cfab44b808d4f Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 26 Sep 2023 10:12:23 +0200 Subject: [PATCH 070/155] ESQL: Document the existing result-set limitations (#99880) Document the newly implicit limit of 500 (if no other limit is provided), as well as the global 10K one. Related: #99816 --- docs/reference/esql/index.asciidoc | 36 +++++++++++-------- .../esql/processing-commands/limit.asciidoc | 3 ++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/reference/esql/index.asciidoc b/docs/reference/esql/index.asciidoc index 7e22eae3ff1b5..ef9399c5e57da 100644 --- a/docs/reference/esql/index.asciidoc +++ b/docs/reference/esql/index.asciidoc @@ -92,6 +92,11 @@ POST /_query?format=txt ---- // TEST[setup:library] +The above query's `LIMIT` command limits results to 5 rows. + +If not specified, `LIMIT` defaults to `500`. A single query will not return +more than 10,000 rows, regardless of the `LIMIT` value. + [discrete] ==== {kib} @@ -105,20 +110,23 @@ with the time filter. [[esql-limitations]] === Limitations -{esql} currently supports the following <>: - -- `alias` -- `boolean` -- `date` -- `double` (`float`, `half_float`, `scaled_float` are represented as `double`) -- `ip` -- `keyword` family including `keyword`, `constant_keyword`, and `wildcard` -- `int` (`short` and `byte` are represented as `int`) -- `long` -- `null` -- `text` -- `unsigned_long` -- `version` +* {esql} currently supports the following <>: + +** `alias` +** `boolean` +** `date` +** `double` (`float`, `half_float`, `scaled_float` are represented as `double`) +** `ip` +** `keyword` family including `keyword`, `constant_keyword`, and `wildcard` +** `int` (`short` and `byte` are represented as `int`) +** `long` +** `null` +** `text` +** `unsigned_long` +** `version` + +* A single query will not return more than 10,000 rows, regardless of the +`LIMIT` command's value. -- include::esql-get-started.asciidoc[] diff --git a/docs/reference/esql/processing-commands/limit.asciidoc b/docs/reference/esql/processing-commands/limit.asciidoc index 963ea2eea37ce..59272fe25614d 100644 --- a/docs/reference/esql/processing-commands/limit.asciidoc +++ b/docs/reference/esql/processing-commands/limit.asciidoc @@ -7,3 +7,6 @@ The `LIMIT` processing command enables you to limit the number of rows: ---- include::{esql-specs}/docs.csv-spec[tag=limit] ---- + +If not specified, `LIMIT` defaults to `500`. A single query will not return +more than 10,000 rows, regardless of the `LIMIT` value. From 3d07d01e429db2e956a0d55b3cb0f296da1630af Mon Sep 17 00:00:00 2001 From: Alexander Spies Date: Tue, 26 Sep 2023 10:15:04 +0200 Subject: [PATCH 071/155] ESQL: Log start and end of queries (#99746) Log ESQL queries both before and after execution so we notice queries that were never completed. --- docs/changelog/99746.yaml | 5 +++++ .../esql/action/EsqlResponseListener.java | 8 ++++++-- .../esql/action/RestEsqlQueryAction.java | 5 +++++ .../esql/plugin/TransportEsqlQueryAction.java | 20 ++++++++++++------- 4 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/99746.yaml diff --git a/docs/changelog/99746.yaml b/docs/changelog/99746.yaml new file mode 100644 index 0000000000000..c4cdbc00f82c1 --- /dev/null +++ b/docs/changelog/99746.yaml @@ -0,0 +1,5 @@ +pr: 99746 +summary: "ESQL: Log start and end of queries" +area: ES|QL +type: enhancement +issues: [] diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java index 4dd0a3ec77419..facff81033cb8 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java @@ -154,12 +154,16 @@ public ActionListener wrapWithLogging() { return ActionListener.wrap(r -> { onResponse(r); // At this point, the StopWatch should already have been stopped, so we log a consistent time. - LOGGER.info("Successfully executed ESQL query in [{}]ms: [{}]", stopWatch.stop().getMillis(), esqlQuery); + LOGGER.info( + "Finished execution of ESQL query.\nQuery string: [{}]\nExecution time: [{}]ms", + esqlQuery, + stopWatch.stop().getMillis() + ); }, ex -> { // In case of failure, stop the time manually before sending out the response. long timeMillis = stopWatch.stop().getMillis(); onFailure(ex); - LOGGER.info("Failed executing ESQL query in [{}]ms: [{}]", timeMillis, esqlQuery); + LOGGER.info("Failed execution of ESQL query.\nQuery string: [{}]\nExecution time: [{}]ms", esqlQuery, timeMillis); }); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/RestEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/RestEsqlQueryAction.java index 15841a00b36de..701d889391d95 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/RestEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/RestEsqlQueryAction.java @@ -9,6 +9,8 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.core.RestApiVersion; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestCancellableNodeClient; @@ -23,6 +25,7 @@ import static org.elasticsearch.xpack.esql.formatter.TextFormat.URL_PARAM_DELIMITER; public class RestEsqlQueryAction extends BaseRestHandler { + private static final Logger LOGGER = LogManager.getLogger(RestEsqlQueryAction.class); @Override public String getName() { @@ -45,6 +48,8 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli esqlRequest = EsqlQueryRequest.fromXContent(parser); } + LOGGER.info("Beginning execution of ESQL query.\nQuery string: [{}]", esqlRequest.query()); + return channel -> { RestCancellableNodeClient cancellableClient = new RestCancellableNodeClient(client, request.getHttpChannel()); cancellableClient.execute( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java index 6cddf5b67af9c..edfae0f91297d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java @@ -105,13 +105,19 @@ private void doExecuteForked(Task task, EsqlQueryRequest request, ActionListener sessionId, configuration, listener.delegateFailureAndWrap( - (delegate, r) -> computeService.execute(sessionId, (CancellableTask) task, r, configuration, delegate.map(pages -> { - List columns = r.output() - .stream() - .map(c -> new ColumnInfo(c.qualifiedName(), EsqlDataTypes.outputType(c.dataType()))) - .toList(); - return new EsqlQueryResponse(columns, pages, request.columnar()); - })) + (delegate, physicalPlan) -> computeService.execute( + sessionId, + (CancellableTask) task, + physicalPlan, + configuration, + delegate.map(pages -> { + List columns = physicalPlan.output() + .stream() + .map(c -> new ColumnInfo(c.qualifiedName(), EsqlDataTypes.outputType(c.dataType()))) + .toList(); + return new EsqlQueryResponse(columns, pages, request.columnar()); + }) + ) ) ); } From accf966cb9fd5396b3e985df9b16b15226630cea Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 26 Sep 2023 09:18:00 +0100 Subject: [PATCH 072/155] [Transform] Remove toVersion method from TransformConfigVersion (#99881) There's no need for a toVersion method in TransformConfigVersion. --- .../validation/SourceDestValidator.java | 26 +++++++++++-------- .../transform/TransformConfigVersion.java | 7 ----- .../transform/transforms/TransformConfig.java | 6 +++-- ...eClusterMinimumVersionValidationTests.java | 23 ++++++++-------- .../TransformConfigVersionTests.java | 11 -------- .../transforms/TransformConfigTests.java | 10 +++---- .../action/TransportStartDatafeedAction.java | 4 +-- .../TransportStartDatafeedActionTests.java | 2 +- 8 files changed, 39 insertions(+), 50 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java index 317a25a573d5a..49bdf60ad0b18 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.core.common.validation; -import org.elasticsearch.Version; +import org.elasticsearch.TransportVersion; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; @@ -70,8 +70,12 @@ public final class SourceDestValidator { public static final String REMOTE_CLUSTER_LICENSE_INACTIVE = "License check failed for remote cluster " + "alias [{0}], license is not active"; public static final String REMOTE_SOURCE_INDICES_NOT_SUPPORTED = "remote source indices are not supported"; - public static final String REMOTE_CLUSTERS_TOO_OLD = - "remote clusters are expected to run at least version [{0}] (reason: [{1}]), but the following clusters were too old: [{2}]"; + public static final String REMOTE_CLUSTERS_TRANSPORT_TOO_OLD = + "remote clusters are expected to run at least transport version [{0}] (reason: [{1}])," + + " but the following clusters were too old: [{2}]"; + public static final String REMOTE_CLUSTERS_CONFIG_TOO_OLD = + "remote clusters are expected to run at least config version [{0}] (reason: [{1}])," + + " but the following clusters were too old: [{2}]"; public static final String PIPELINE_MISSING = "Pipeline with id [{0}] could not be found"; private final IndexNameExpressionResolver indexNameExpressionResolver; @@ -223,8 +227,8 @@ public Set getRegisteredRemoteClusterNames() { } // convenience method to make testing easier - public Version getRemoteClusterVersion(String cluster) { - return remoteClusterService.getConnection(cluster).getVersion(); + public TransportVersion getRemoteClusterVersion(String cluster) { + return remoteClusterService.getConnection(cluster).getTransportVersion(); } private void resolveLocalAndRemoteSource() { @@ -448,15 +452,15 @@ public void validate(Context context, ActionListener listener) { public static class RemoteClusterMinimumVersionValidation implements SourceDestValidation { - private final Version minExpectedVersion; + private final TransportVersion minExpectedVersion; private final String reason; - public RemoteClusterMinimumVersionValidation(Version minExpectedVersion, String reason) { + public RemoteClusterMinimumVersionValidation(TransportVersion minExpectedVersion, String reason) { this.minExpectedVersion = minExpectedVersion; this.reason = reason; } - public Version getMinExpectedVersion() { + public TransportVersion getMinExpectedTransportVersion() { return minExpectedVersion; } @@ -467,7 +471,7 @@ public String getReason() { @Override public void validate(Context context, ActionListener listener) { List remoteIndices = new ArrayList<>(context.resolveRemoteSource()); - Map remoteClusterVersions; + Map remoteClusterVersions; try { List remoteAliases = RemoteClusterLicenseChecker.remoteClusterAliases( context.getRegisteredRemoteClusterNames(), @@ -483,13 +487,13 @@ public void validate(Context context, ActionListener listener) { listener.onResponse(context); return; } - Map oldRemoteClusterVersions = remoteClusterVersions.entrySet() + Map oldRemoteClusterVersions = remoteClusterVersions.entrySet() .stream() .filter(entry -> entry.getValue().before(minExpectedVersion)) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); if (oldRemoteClusterVersions.isEmpty() == false) { context.addValidationError( - REMOTE_CLUSTERS_TOO_OLD, + REMOTE_CLUSTERS_TRANSPORT_TOO_OLD, minExpectedVersion, reason, oldRemoteClusterVersions.entrySet() diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java index 1265f9b47f518..acc10008cd40f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java @@ -312,13 +312,6 @@ public static TransformConfigVersion fromVersion(Version version) { return fromId(version.id); } - public static Version toVersion(TransformConfigVersion TransformConfigVersion) { - if (TransformConfigVersion.before(FIRST_TRANSFORM_VERSION) || TransformConfigVersion.onOrAfter(V_8_10_0)) { - throw new IllegalArgumentException("Cannot convert " + TransformConfigVersion + ". Incompatible version"); - } - return Version.fromId(TransformConfigVersion.id); - } - public static TransformConfigVersion getMinTransformConfigVersion(DiscoveryNodes nodes) { return getMinMaxTransformConfigVersion(nodes).v1(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java index 63197f7903004..6a33a3beaa191 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfig.java @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.core.transform.transforms; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.cluster.SimpleDiffable; import org.elasticsearch.common.Strings; @@ -62,7 +64,7 @@ public class TransformConfig implements SimpleDiffable, Writeab public static final String NAME = "data_frame_transform_config"; public static final ParseField HEADERS = new ParseField("headers"); /** Version in which {@code FieldCapabilitiesRequest.runtime_fields} field was introduced. */ - private static final TransformConfigVersion FIELD_CAPS_RUNTIME_MAPPINGS_INTRODUCED_VERSION = TransformConfigVersion.V_7_12_0; + private static final TransportVersion FIELD_CAPS_RUNTIME_MAPPINGS_INTRODUCED_TRANSPORT_VERSION = TransportVersions.V_7_12_0; /** Specifies all the possible transform functions. */ public enum Function { @@ -343,7 +345,7 @@ public RetentionPolicyConfig getRetentionPolicyConfig() { public List getAdditionalSourceDestValidations() { if ((source.getRuntimeMappings() == null || source.getRuntimeMappings().isEmpty()) == false) { SourceDestValidation validation = new SourceDestValidator.RemoteClusterMinimumVersionValidation( - TransformConfigVersion.toVersion(FIELD_CAPS_RUNTIME_MAPPINGS_INTRODUCED_VERSION), + FIELD_CAPS_RUNTIME_MAPPINGS_INTRODUCED_TRANSPORT_VERSION, "source.runtime_mappings field was set" ); return Collections.singletonList(validation); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/RemoteClusterMinimumVersionValidationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/RemoteClusterMinimumVersionValidationTests.java index f6d1863a387a1..57166d996d124 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/RemoteClusterMinimumVersionValidationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/validation/RemoteClusterMinimumVersionValidationTests.java @@ -7,7 +7,8 @@ package org.elasticsearch.xpack.core.common.validation; -import org.elasticsearch.Version; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.support.ActionTestUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.NoSuchRemoteClusterException; @@ -32,7 +33,7 @@ public class RemoteClusterMinimumVersionValidationTests extends ESTestCase { - private static final Version MIN_EXPECTED_VERSION = Version.V_7_11_0; + private static final TransportVersion MIN_EXPECTED_VERSION = TransportVersions.V_7_11_0; private static final String REASON = "some reason"; private Context context; @@ -40,14 +41,14 @@ public class RemoteClusterMinimumVersionValidationTests extends ESTestCase { @Before public void setUpMocks() { context = spy(new Context(null, null, null, null, null, null, null, null, null, null)); - doReturn(Version.V_7_10_2).when(context).getRemoteClusterVersion("cluster-A"); - doReturn(Version.V_7_11_0).when(context).getRemoteClusterVersion("cluster-B"); - doReturn(Version.V_7_11_2).when(context).getRemoteClusterVersion("cluster-C"); + doReturn(TransportVersions.V_7_10_0).when(context).getRemoteClusterVersion("cluster-A"); + doReturn(TransportVersions.V_7_11_0).when(context).getRemoteClusterVersion("cluster-B"); + doReturn(TransportVersions.V_7_12_0).when(context).getRemoteClusterVersion("cluster-C"); } public void testGetters() { RemoteClusterMinimumVersionValidation validation = new RemoteClusterMinimumVersionValidation(MIN_EXPECTED_VERSION, REASON); - assertThat(validation.getMinExpectedVersion(), is(equalTo(MIN_EXPECTED_VERSION))); + assertThat(validation.getMinExpectedTransportVersion(), is(equalTo(MIN_EXPECTED_VERSION))); assertThat(validation.getReason(), is(equalTo(REASON))); } @@ -81,8 +82,8 @@ public void testValidate_OneRemoteClusterVersionTooLow() { ctx -> assertThat( ctx.getValidationException().validationErrors(), contains( - "remote clusters are expected to run at least version [7.11.0] (reason: [some reason]), " - + "but the following clusters were too old: [cluster-A (7.10.2)]" + "remote clusters are expected to run at least transport version [7110099] (reason: [some reason]), " + + "but the following clusters were too old: [cluster-A (7100099)]" ) ) ) @@ -92,15 +93,15 @@ public void testValidate_OneRemoteClusterVersionTooLow() { public void testValidate_TwoRemoteClusterVersionsTooLow() { doReturn(new HashSet<>(Arrays.asList("cluster-A", "cluster-B", "cluster-C"))).when(context).getRegisteredRemoteClusterNames(); doReturn(new TreeSet<>(Arrays.asList("cluster-A:dummy", "cluster-B:dummy", "cluster-C:dummy"))).when(context).resolveRemoteSource(); - SourceDestValidation validation = new RemoteClusterMinimumVersionValidation(Version.V_7_11_2, REASON); + SourceDestValidation validation = new RemoteClusterMinimumVersionValidation(TransportVersions.V_7_12_0, REASON); validation.validate( context, ActionTestUtils.assertNoFailureListener( ctx -> assertThat( ctx.getValidationException().validationErrors(), contains( - "remote clusters are expected to run at least version [7.11.2] (reason: [some reason]), " - + "but the following clusters were too old: [cluster-A (7.10.2), cluster-B (7.11.0)]" + "remote clusters are expected to run at least transport version [7120099] (reason: [some reason]), " + + "but the following clusters were too old: [cluster-A (7100099), cluster-B (7110099)]" ) ) ) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java index b18243a9e4e0e..01c7e9c12b427 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java @@ -264,17 +264,6 @@ public void testFromVersion() { assertEquals("Cannot convert " + Version.fromId(8_11_00_99) + ". Incompatible version", e.getMessage()); } - public void testToVersion() { - TransformConfigVersion TransformConfigVersion_V_7_7_0 = TransformConfigVersion.V_7_7_0; - Version version_V_7_7_0 = TransformConfigVersion.toVersion(TransformConfigVersion_V_7_7_0); - assertEquals(version_V_7_7_0.id, TransformConfigVersion_V_7_7_0.id()); - - // There's no mapping between Version and TransformConfigVersion values from TransformConfigVersion.V_10 onwards. - TransformConfigVersion TransformConfigVersion_V_10 = TransformConfigVersion.V_10; - Exception e = expectThrows(IllegalArgumentException.class, () -> TransformConfigVersion.toVersion(TransformConfigVersion_V_10)); - assertEquals("Cannot convert " + TransformConfigVersion_V_10 + ". Incompatible version", e.getMessage()); - } - public void testVersionConstantPresent() { Set ignore = Set.of( TransformConfigVersion.ZERO, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigTests.java index 66c030eeb09d4..ba2cd0ba04312 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformConfigTests.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.core.transform.transforms; -import org.elasticsearch.Version; +import org.elasticsearch.TransportVersions; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -603,7 +603,7 @@ public void testRewriteForUpdate() throws IOException { "max_page_search_size": 111 }, "version": "%s" - }""", Version.V_7_6_0.toString()); + }""", TransformConfigVersion.V_7_6_0.toString()); TransformConfig transformConfig = createTransformConfigFromString(pivotTransform, "body_id", true); TransformConfig transformConfigRewritten = TransformConfig.rewriteForUpdate(transformConfig); @@ -645,7 +645,7 @@ public void testRewriteForUpdateAlignCheckpoints() throws IOException { } }, "version": "%s" - }""", Version.V_7_12_0.toString()); + }""", TransformConfigVersion.V_7_12_0.toString()); TransformConfig transformConfig = createTransformConfigFromString(pivotTransform, "body_id", true); TransformConfig transformConfigRewritten = TransformConfig.rewriteForUpdate(transformConfig); @@ -693,7 +693,7 @@ public void testRewriteForUpdateMaxPageSizeSearchConflicting() throws IOExceptio "max_page_search_size": 555 }, "version": "%s" - }""", Version.V_7_5_0.toString()); + }""", TransformConfigVersion.V_7_5_0.toString()); TransformConfig transformConfig = createTransformConfigFromString(pivotTransform, "body_id", true); TransformConfig transformConfigRewritten = TransformConfig.rewriteForUpdate(transformConfig); @@ -828,7 +828,7 @@ public void testGetAdditionalSourceDestValidations_WithRuntimeMappings() throws assertThat(additiionalValidations.get(0), is(instanceOf(RemoteClusterMinimumVersionValidation.class))); RemoteClusterMinimumVersionValidation remoteClusterMinimumVersionValidation = (RemoteClusterMinimumVersionValidation) additiionalValidations.get(0); - assertThat(remoteClusterMinimumVersionValidation.getMinExpectedVersion(), is(equalTo(Version.V_7_12_0))); + assertThat(remoteClusterMinimumVersionValidation.getMinExpectedTransportVersion(), is(equalTo(TransportVersions.V_7_12_0))); assertThat(remoteClusterMinimumVersionValidation.getReason(), is(equalTo("source.runtime_mappings field was set"))); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java index 4028054368907..66ad07e76bca1 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java @@ -79,7 +79,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.REMOTE_CLUSTERS_TOO_OLD; +import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.REMOTE_CLUSTERS_CONFIG_TOO_OLD; /* This class extends from TransportMasterNodeAction for cluster state observing purposes. The stop datafeed api also redirect the elected master node. @@ -316,7 +316,7 @@ static void checkRemoteConfigVersions( throw ExceptionsHelper.badRequestException( Messages.getMessage( - REMOTE_CLUSTERS_TOO_OLD, + REMOTE_CLUSTERS_CONFIG_TOO_OLD, minVersion.toString(), reason, Strings.collectionToCommaDelimitedString(clustersTooOld) diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java index 1d1d99d028f97..4a66a2836273e 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java @@ -137,7 +137,7 @@ public void testRemoteClusterVersionCheck() { assertThat( ex.getMessage(), containsString( - "remote clusters are expected to run at least version [7.11.0] (reason: [runtime_mappings]), " + "remote clusters are expected to run at least config version [7.11.0] (reason: [runtime_mappings]), " + "but the following clusters were too old: [old_cluster_1]" ) ); From b08f7b7e97f7ef259bc39584e0a10e1d92f53d21 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 26 Sep 2023 09:18:16 +0100 Subject: [PATCH 073/155] [ML] Remove toVersion method from MlConfigVersion (#99884) There's no need for a toVersion method in MlConfigVersion. --- .../elasticsearch/xpack/core/ml/MlConfigVersion.java | 7 ------- .../xpack/core/ml/MlConfigVersionTests.java | 11 ----------- 2 files changed, 18 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java index 37446a5551fd2..75370db1d766f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java @@ -278,13 +278,6 @@ public static MlConfigVersion fromVersion(Version version) { return fromId(version.id); } - public static Version toVersion(MlConfigVersion mlConfigVersion) { - if (mlConfigVersion.before(FIRST_ML_VERSION) || mlConfigVersion.onOrAfter(V_8_10_0)) { - throw new IllegalArgumentException("Cannot convert " + mlConfigVersion + ". Incompatible version"); - } - return Version.fromId(mlConfigVersion.id); - } - public static MlConfigVersion getMinMlConfigVersion(DiscoveryNodes nodes) { return getMinMaxMlConfigVersion(nodes).v1(); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java index 575de8c0faf0d..6ead94bbc1fdb 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java @@ -250,17 +250,6 @@ public void testFromVersion() { assertEquals("Cannot convert " + Version.fromId(8_11_00_99) + ". Incompatible version", e.getMessage()); } - public void testToVersion() { - MlConfigVersion mlConfigVersion_V_7_7_0 = MlConfigVersion.V_7_0_0; - Version version_V_7_7_0 = MlConfigVersion.toVersion(mlConfigVersion_V_7_7_0); - assertEquals(version_V_7_7_0.id, mlConfigVersion_V_7_7_0.id()); - - // There's no mapping between Version and MlConfigVersion values from MlConfigVersion.V_10 onwards. - MlConfigVersion mlConfigVersion_V_10 = MlConfigVersion.V_10; - Exception e = expectThrows(IllegalArgumentException.class, () -> MlConfigVersion.toVersion(mlConfigVersion_V_10)); - assertEquals("Cannot convert " + mlConfigVersion_V_10 + ". Incompatible version", e.getMessage()); - } - public void testVersionConstantPresent() { Set ignore = Set.of(MlConfigVersion.ZERO, MlConfigVersion.CURRENT, MlConfigVersion.FIRST_ML_VERSION); assertThat(MlConfigVersion.CURRENT, sameInstance(MlConfigVersion.fromId(MlConfigVersion.CURRENT.id()))); From e3f1c15788d856e04be68fce97803e8bc53a4af1 Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Tue, 26 Sep 2023 02:18:24 -0700 Subject: [PATCH 074/155] Update ml-delayed-data-detection.asciidoc (#99843) --- .../ml/anomaly-detection/ml-delayed-data-detection.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/ml/anomaly-detection/ml-delayed-data-detection.asciidoc b/docs/reference/ml/anomaly-detection/ml-delayed-data-detection.asciidoc index a5a7b01f095ac..5d72b4682d4ea 100644 --- a/docs/reference/ml/anomaly-detection/ml-delayed-data-detection.asciidoc +++ b/docs/reference/ml/anomaly-detection/ml-delayed-data-detection.asciidoc @@ -22,6 +22,7 @@ the value of `query_delay'. If it doesn't help, investigate the ingest latency a cause. You can do this by comparing event and ingest timestamps. High latency is often caused by bursts of ingested documents, misconfiguration of the ingest pipeline, or misalignment of system clocks. + == Why worry about delayed data? If data are delayed randomly (and consequently are missing from analysis), the From 1e28d102947131049cd598bf70185036f50c1356 Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:41:56 +0100 Subject: [PATCH 075/155] ESQL: Update ProjectOperator to release dropped blocks (#99885) This commit updates the ProjectOperator to release dropped blocks. --- .../compute/operator/ProjectOperator.java | 14 ++++++- .../compute/operator/AnyOperatorTestCase.java | 2 +- .../compute/operator/OperatorTestCase.java | 1 + .../operator/ProjectOperatorTests.java | 39 +++++++++++++++++-- .../operator/TupleBlockSourceOperator.java | 18 ++++++--- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java index 4192bfd570bd4..a4b73d6277271 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java @@ -9,9 +9,13 @@ import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; +import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.List; public class ProjectOperator extends AbstractPageMappingOperator { @@ -52,10 +56,16 @@ protected Page process(Page page) { Arrays.fill(blocks, null); int b = 0; int positionCount = page.getPositionCount(); - for (int i = bs.nextSetBit(0); i >= 0 && i < page.getBlockCount(); i = bs.nextSetBit(i + 1)) { + List blocksToRelease = new ArrayList<>(); + for (int i = 0; i < page.getBlockCount(); i++) { var block = page.getBlock(i); - blocks[b++] = block; + if (bs.get(i)) { + blocks[b++] = block; + } else { + blocksToRelease.add(block); + } } + Releasables.close(blocksToRelease); return new Page(positionCount, blocks); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java index edbc59f9497fc..8f995d9a31bc3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java @@ -95,7 +95,7 @@ protected final BigArrays nonBreakingBigArrays() { /** * A {@link DriverContext} with a nonBreakingBigArrays. */ - protected final DriverContext driverContext() { + protected DriverContext driverContext() { return new DriverContext(nonBreakingBigArrays(), BlockFactory.getNonBreakingInstance()); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java index 6a2ace060e1e6..3cbab148e3073 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java @@ -143,6 +143,7 @@ private void assertSimple(DriverContext context, int size) { BigArrays bigArrays = context.bigArrays().withCircuitBreaking(); List results = drive(simple(bigArrays).get(context), input.iterator()); assertSimpleOutput(input, results); + results.forEach(Page::releaseBlocks); assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index 691c6f6cdbf56..d4d7095d92f7b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -7,22 +7,47 @@ package org.elasticsearch.compute.operator; +import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantIntVector; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.core.Tuple; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.junit.After; +import org.junit.Before; import java.util.BitSet; import java.util.List; import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ProjectOperatorTests extends OperatorTestCase { + + final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + final BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); + + @Before + @After + public void assertBreakerIsZero() { + assertThat(breaker.getUsed(), is(0L)); + } + + @Override + protected DriverContext driverContext() { + return new DriverContext(blockFactory.bigArrays(), blockFactory); + } + public void testProjectionOnEmptyPage() { var page = new Page(0); var projection = new ProjectOperator(randomMask(randomIntBetween(2, 10))); @@ -34,7 +59,7 @@ public void testProjection() { var size = randomIntBetween(2, 5); var blocks = new Block[size]; for (int i = 0; i < blocks.length; i++) { - blocks[i] = new ConstantIntVector(i, size).asBlock(); + blocks[i] = blockFactory.newConstantIntBlockWith(i, size); } var page = new Page(size, blocks); @@ -52,6 +77,7 @@ public void testProjection() { assertTrue(mask.get(shouldBeSetInMask)); lastSetIndex = mask.nextSetBit(lastSetIndex + 1); assertEquals(shouldBeSetInMask, lastSetIndex); + block.close(); } } @@ -65,7 +91,7 @@ private BitSet randomMask(int size) { @Override protected SourceOperator simpleInput(int end) { - return new TupleBlockSourceOperator(LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); + return new TupleBlockSourceOperator(blockFactory, LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); } @Override @@ -106,4 +132,11 @@ protected ByteSizeValue smallEnoughToCircuitBreak() { assumeTrue("doesn't use big arrays so can't break", false); return null; } + + // A breaker service that always returns the given breaker for getBreaker(CircuitBreaker.REQUEST) + static CircuitBreakerService mockBreakerService(CircuitBreaker breaker) { + CircuitBreakerService breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return breakerService; + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java index 0bcaac0e5b646..78cff5897c917 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java @@ -7,7 +7,7 @@ package org.elasticsearch.compute.operator; -import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import org.elasticsearch.core.Tuple; @@ -22,14 +22,21 @@ public class TupleBlockSourceOperator extends AbstractBlockSourceOperator { private static final int DEFAULT_MAX_PAGE_POSITIONS = 8 * 1024; + private final BlockFactory blockFactory; + private final List> values; public TupleBlockSourceOperator(Stream> values) { - this(values, DEFAULT_MAX_PAGE_POSITIONS); + this(BlockFactory.getNonBreakingInstance(), values, DEFAULT_MAX_PAGE_POSITIONS); + } + + public TupleBlockSourceOperator(BlockFactory blockFactory, Stream> values) { + this(blockFactory, values, DEFAULT_MAX_PAGE_POSITIONS); } - public TupleBlockSourceOperator(Stream> values, int maxPagePositions) { + public TupleBlockSourceOperator(BlockFactory blockFactory, Stream> values, int maxPagePositions) { super(maxPagePositions); + this.blockFactory = blockFactory; this.values = values.toList(); } @@ -40,12 +47,13 @@ public TupleBlockSourceOperator(List> values) { public TupleBlockSourceOperator(List> values, int maxPagePositions) { super(maxPagePositions); this.values = values; + blockFactory = BlockFactory.getNonBreakingInstance(); } @Override protected Page createPage(int positionOffset, int length) { - var blockBuilder1 = LongBlock.newBlockBuilder(length); - var blockBuilder2 = LongBlock.newBlockBuilder(length); + var blockBuilder1 = blockFactory.newLongBlockBuilder(length); + var blockBuilder2 = blockFactory.newLongBlockBuilder(length); for (int i = 0; i < length; i++) { Tuple item = values.get(positionOffset + i); if (item.v1() == null) { From f6dce12be85aa36f3b52e4e09202335d01a03fe3 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 26 Sep 2023 20:54:00 +1000 Subject: [PATCH 076/155] Adjust threadContext handling for SingleResultDeduplicator (#99728) The PR adjust the threadContext handling for SingleResultDeduplicator to ensure that: 1. Listeners are always completed in their original threadContext regardless their order of execution 2. A listener always has response headers propagated back from a single work execution associated with that listener --- .../action/SingleResultDeduplicator.java | 56 ++++++++++++---- .../SingleResultDeduplicatorTests.java | 67 +++++++++++++++---- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/SingleResultDeduplicator.java b/server/src/main/java/org/elasticsearch/action/SingleResultDeduplicator.java index 273c542bc825c..aa07a11ba124e 100644 --- a/server/src/main/java/org/elasticsearch/action/SingleResultDeduplicator.java +++ b/server/src/main/java/org/elasticsearch/action/SingleResultDeduplicator.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.support.ContextPreservingActionListener; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.core.Nullable; import java.util.ArrayList; import java.util.List; @@ -35,6 +36,11 @@ public final class SingleResultDeduplicator { * up here once done. */ private List> waitingListeners; + /** + * The threadContext associated with the first listener in the waitingListeners. This context will be restored right before + * we perform the {@code executeAction}. + */ + private ThreadContext.StoredContext waitingStoredContext; private final Consumer> executeAction; @@ -45,7 +51,9 @@ public SingleResultDeduplicator(ThreadContext threadContext, Consumer listener) { synchronized (this) { @@ -53,42 +61,64 @@ public void execute(ActionListener listener) { // no queued up listeners, just execute this one directly without deduplication and instantiate the list so that // subsequent executions will wait waitingListeners = new ArrayList<>(); + waitingStoredContext = null; } else { // already running an execution, queue this one up + if (waitingListeners.isEmpty()) { + // Only the first listener in queue needs the stored context which is used for running executeAction + assert waitingStoredContext == null; + waitingStoredContext = threadContext.newStoredContext(); + } waitingListeners.add(ContextPreservingActionListener.wrapPreservingContext(listener, threadContext)); return; } } - doExecute(listener); + doExecute(ContextPreservingActionListener.wrapPreservingContext(listener, threadContext), null); } - private void doExecute(ActionListener listener) { + private void doExecute(ActionListener listener, @Nullable ThreadContext.StoredContext storedContext) { final ActionListener wrappedListener = ActionListener.runBefore(listener, () -> { final List> listeners; + final ThreadContext.StoredContext thisStoredContext; synchronized (this) { if (waitingListeners.isEmpty()) { // no listeners were queued up while this execution ran, so we just reset the state to not having a running execution waitingListeners = null; + waitingStoredContext = null; return; } else { // we have queued up listeners, so we create a fresh list for the next execution and execute once to handle the // listeners currently queued up listeners = waitingListeners; + thisStoredContext = waitingStoredContext; + // This batch of listeners will use the context of the first listener in this batch for the work execution + assert thisStoredContext != null : "stored context must not be null for the first listener in a batch"; waitingListeners = new ArrayList<>(); + waitingStoredContext = null; } } - doExecute(new ActionListener<>() { - @Override - public void onResponse(T response) { - ActionListener.onResponse(listeners, response); - } - @Override - public void onFailure(Exception e) { - ActionListener.onFailure(listeners, e); - } - }); + // Create a child threadContext so that the parent context remains unchanged when the child execution ends. + // This ensures the parent does not see response headers from the child execution. + try (var ignore = threadContext.newStoredContext()) { + doExecute(new ActionListener<>() { + @Override + public void onResponse(T response) { + ActionListener.onResponse(listeners, response); + } + + @Override + public void onFailure(Exception e) { + ActionListener.onFailure(listeners, e); + } + }, thisStoredContext); + } }); + // Restore the given threadContext before proceed with the work execution. + // This ensures all executions begin execution with their own context. + if (storedContext != null) { + storedContext.restore(); + } ActionListener.run(wrappedListener, executeAction::accept); } } diff --git a/server/src/test/java/org/elasticsearch/transport/SingleResultDeduplicatorTests.java b/server/src/test/java/org/elasticsearch/transport/SingleResultDeduplicatorTests.java index fb4c9df512a5a..cfc1d1888e024 100644 --- a/server/src/test/java/org/elasticsearch/transport/SingleResultDeduplicatorTests.java +++ b/server/src/test/java/org/elasticsearch/transport/SingleResultDeduplicatorTests.java @@ -22,8 +22,20 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; public class SingleResultDeduplicatorTests extends ESTestCase { @@ -88,11 +100,27 @@ public void onFailure(Exception e) { public void testThreadContextPreservation() { final var resources = new Releasable[1]; try { + + final var workerResponseHeaderValueCounter = new AtomicInteger(); + final List allSeenResponseHeaderValues = Collections.synchronizedList(new ArrayList<>()); + final List allSeenThreadHeaderValues = Collections.synchronizedList(new ArrayList<>()); + + final var threads = between(1, 5); final var future = new PlainActionFuture(); try (var listeners = new RefCountingListener(future)) { + final var workerRequestHeaderName = "worker-request-header"; + final var workerResponseHeaderName = "worker-response-header"; + final var threadHeaderName = "test-header"; final var threadContext = new ThreadContext(Settings.EMPTY); - final var deduplicator = new SingleResultDeduplicator(threadContext, l -> l.onResponse(null)); - final var threads = between(1, 5); + final var deduplicator = new SingleResultDeduplicator(threadContext, l -> { + threadContext.putHeader(workerRequestHeaderName, randomAlphaOfLength(5)); + threadContext.addResponseHeader( + workerResponseHeaderName, + String.valueOf(workerResponseHeaderValueCounter.getAndIncrement()) + ); + allSeenThreadHeaderValues.add(Integer.valueOf(threadContext.getHeader(threadHeaderName))); + l.onResponse(null); + }); final var executor = EsExecutors.newFixed( "test", threads, @@ -103,24 +131,35 @@ public void testThreadContextPreservation() { ); resources[0] = () -> ThreadPool.terminate(executor, 10, TimeUnit.SECONDS); final var barrier = new CyclicBarrier(threads); - final var headerName = "test-header"; for (int i = 0; i < threads; i++) { try (var ignored = threadContext.stashContext()) { - final var headerValue = randomAlphaOfLength(10); - threadContext.putHeader(headerName, headerValue); - executor.execute( - ActionRunnable.wrap( - listeners.acquire(v -> assertEquals(headerValue, threadContext.getHeader(headerName))), - listener -> { - safeAwait(barrier); - deduplicator.execute(listener); - } - ) - ); + final var threadHeaderValue = String.valueOf(i); + threadContext.putHeader(threadHeaderName, threadHeaderValue); + executor.execute(ActionRunnable.wrap(listeners.acquire(v -> { + // original request header before the work execution should be preserved + assertEquals(threadHeaderValue, threadContext.getHeader(threadHeaderName)); + // request header used by the work execution should *not* be preserved + assertThat(threadContext.getHeaders(), not(hasKey(workerRequestHeaderName))); + // response header should be preserved which is from a single execution of the work + final List responseHeader = threadContext.getResponseHeaders().get(workerResponseHeaderName); + assertThat(responseHeader, hasSize(1)); + allSeenResponseHeaderValues.add(Integer.valueOf(responseHeader.get(0))); + }), listener -> { + safeAwait(barrier); + deduplicator.execute(listener); + })); } } } future.actionGet(10, TimeUnit.SECONDS); + assertThat(allSeenResponseHeaderValues, hasSize(threads)); + // The total number of observed response header values consistent with how many times it is generated + assertThat( + Set.copyOf(allSeenResponseHeaderValues), + equalTo(IntStream.range(0, workerResponseHeaderValueCounter.get()).boxed().collect(Collectors.toUnmodifiableSet())) + ); + // The following proves each work execution will see a different thread's context in that execution batch + assertThat(Set.copyOf(allSeenThreadHeaderValues), hasSize(workerResponseHeaderValueCounter.get())); } finally { Releasables.closeExpectNoException(resources); } From 6ec7a0d405a9a68d602dd6c610a1f53904a044c3 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Tue, 26 Sep 2023 12:54:35 +0200 Subject: [PATCH 077/155] Remove method Netty4HttpPipeliningHandlerTests#shutdownExecutorService (#99901) In the next Lucene version it has been introduced a static method on LuceneTestCase called shutdownExecutorService. This static method will clash with the method in Netty4HttpPipeliningHandlerTests and it is making the lucene_snapshot branch unhappy. As the method is only called from the tearDown method, lets move the code there. --- .../Netty4HttpPipeliningHandlerTests.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java index 895c07ec7a3f6..aa6e51cf9fb00 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpPipeliningHandlerTests.java @@ -70,16 +70,7 @@ public class Netty4HttpPipeliningHandlerTests extends ESTestCase { @After public void tearDown() throws Exception { waitingRequests.keySet().forEach(this::finishRequest); - shutdownExecutorService(); - super.tearDown(); - } - - private CountDownLatch finishRequest(String url) { - waitingRequests.get(url).countDown(); - return finishingRequests.get(url); - } - - private void shutdownExecutorService() throws InterruptedException { + // shutdown the Executor Service if (handlerService.isShutdown() == false) { handlerService.shutdown(); handlerService.awaitTermination(10, TimeUnit.SECONDS); @@ -88,6 +79,12 @@ private void shutdownExecutorService() throws InterruptedException { eventLoopService.shutdown(); eventLoopService.awaitTermination(10, TimeUnit.SECONDS); } + super.tearDown(); + } + + private CountDownLatch finishRequest(String url) { + waitingRequests.get(url).countDown(); + return finishingRequests.get(url); } public void testThatPipeliningWorksWithFastSerializedRequests() throws InterruptedException { From e0cc375b14f3e530bfeed6f04d483deb13a79322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Tue, 26 Sep 2023 12:58:19 +0200 Subject: [PATCH 078/155] [DOCS] Adds text_expansion config to inference processor reference docs. (#99900) --- .../ingest/processors/inference.asciidoc | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/docs/reference/ingest/processors/inference.asciidoc b/docs/reference/ingest/processors/inference.asciidoc index 12702255df10d..9b358643df734 100644 --- a/docs/reference/ingest/processors/inference.asciidoc +++ b/docs/reference/ingest/processors/inference.asciidoc @@ -318,6 +318,72 @@ include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenizati ======= ===== + +[discrete] +[[inference-processor-text-expansion-opt]] +==== Text expansion configuration options + +`results_field`:: +(Optional, string) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-results-field-processor] + + +`tokenization`:: +(Optional, object) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization] ++ +.Properties of tokenization +[%collapsible%open] +===== +`bert`:::: +(Optional, object) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-bert] ++ +.Properties of bert +[%collapsible%open] +======= + +`span`:::: +(Optional, integer) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-span] + +`truncate`:::: +(Optional, string) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-truncate] +======= + +`roberta`:::: +(Optional, object) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-roberta] ++ +.Properties of roberta +[%collapsible%open] +======= + +`span`:::: +(Optional, integer) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-span] + +`truncate`:::: +(Optional, string) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-truncate] +======= + +`mpnet`:::: +(Optional, object) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-mpnet] ++ +.Properties of mpnet +[%collapsible%open] +======= + +`truncate`:::: +(Optional, string) +include::{es-repo-dir}/ml/ml-shared.asciidoc[tag=inference-config-nlp-tokenization-truncate] +======= +===== + + [discrete] [[inference-processor-zero-shot-opt]] ==== Zero shot classification configuration options From 137bb4566263ce5f1043026a4ab02c1ef934a56a Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Tue, 26 Sep 2023 14:29:56 +0300 Subject: [PATCH 079/155] Support runtime fields in synthetic source (#99796) * Support runtime fields in synthetic source * Update docs/changelog/99796.yaml * Introduce SyntheticSourceProvider * Address comments * More fixes * Fix checkstyle violation * More unittest updates * Use SourceProvider in MapperServiceTestCase * Remove runtime field from unittest * Update synthetic source doc --- docs/changelog/99796.yaml | 6 + .../mapping/fields/synthetic-source.asciidoc | 8 +- .../runtime_fields/270_synthetic_source.yml | 134 ++++++++++++++++++ ...dNumericDocValuesSyntheticFieldLoader.java | 2 +- ...ortedSetDocValuesSyntheticFieldLoader.java | 2 +- .../index/mapper/SourceLoader.java | 2 + .../index/query/SearchExecutionContext.java | 6 +- .../search/lookup/SourceProvider.java | 12 ++ .../lookup/SyntheticSourceProvider.java | 77 ++++++++++ .../query/SearchExecutionContextTests.java | 30 ++-- .../index/mapper/MapperServiceTestCase.java | 7 +- 11 files changed, 254 insertions(+), 32 deletions(-) create mode 100644 docs/changelog/99796.yaml create mode 100644 modules/runtime-fields-common/src/yamlRestTest/resources/rest-api-spec/test/runtime_fields/270_synthetic_source.yml create mode 100644 server/src/main/java/org/elasticsearch/search/lookup/SyntheticSourceProvider.java diff --git a/docs/changelog/99796.yaml b/docs/changelog/99796.yaml new file mode 100644 index 0000000000000..cad10564ed294 --- /dev/null +++ b/docs/changelog/99796.yaml @@ -0,0 +1,6 @@ +pr: 99796 +summary: Support runtime fields in synthetic source +area: Aggregations +type: bug +issues: + - 98287 diff --git a/docs/reference/mapping/fields/synthetic-source.asciidoc b/docs/reference/mapping/fields/synthetic-source.asciidoc index 5da334b883721..342372e1f3227 100644 --- a/docs/reference/mapping/fields/synthetic-source.asciidoc +++ b/docs/reference/mapping/fields/synthetic-source.asciidoc @@ -28,7 +28,7 @@ PUT idx While this on the fly reconstruction is *generally* slower than saving the source documents verbatim and loading them at query time, it saves a lot of storage -space. +space. [[synthetic-source-restrictions]] ===== Synthetic `_source` restrictions @@ -37,12 +37,6 @@ There are a couple of restrictions to be aware of: * When you retrieve synthetic `_source` content it undergoes minor <> compared to the original JSON. -* The `params._source` is unavailable in scripts. Instead use the -{painless}/painless-field-context.html[`doc`] API or the <>. -* Runtime fields <>, and runtime -fields that access `_source` are currently not supported for indices that use -synthetic `_source`. Use a scripted runtime field that accesses fields <> or the -<> instead. * Synthetic `_source` can be used with indices that contain only these field types: diff --git a/modules/runtime-fields-common/src/yamlRestTest/resources/rest-api-spec/test/runtime_fields/270_synthetic_source.yml b/modules/runtime-fields-common/src/yamlRestTest/resources/rest-api-spec/test/runtime_fields/270_synthetic_source.yml new file mode 100644 index 0000000000000..ac2a56cf26b19 --- /dev/null +++ b/modules/runtime-fields-common/src/yamlRestTest/resources/rest-api-spec/test/runtime_fields/270_synthetic_source.yml @@ -0,0 +1,134 @@ +--- +keywords: + - skip: + version: " - 7.11.99" + reason: Runtime mappings support was added in 7.12 + + - do: + indices.create: + index: index1 + body: + mappings: + _source: + mode: synthetic + properties: + field1: + type: keyword + field2: + type: keyword + + - do: + search: + body: + size: 0 + runtime_mappings: + field3: + type: keyword + script: + source: "emit(params._source.field1 + params._source.field2)" + aggs: + field3: + terms: + field: field3 + + - length: { aggregations.field3.buckets: 0 } + + - do: + bulk: + index: index1 + refresh: true + body: | + {"index":{}} + { "field1": "A", "field2": "B" } + {"index":{}} + { "field1": "A", "field2": "B" } + {"index":{}} + { "field1": "C", "field2": "D" } + + - do: + search: + body: + size: 0 + runtime_mappings: + field3: + type: keyword + script: + source: "emit(params._source.field1 + params._source.field2)" + aggs: + field3: + terms: + field: field3 + + - length: { aggregations.field3.buckets: 2 } + - match: { aggregations.field3.buckets.0.key: AB } + - match: { aggregations.field3.buckets.0.doc_count: 2 } + - match: { aggregations.field3.buckets.1.key: CD } + - match: { aggregations.field3.buckets.1.doc_count: 1 } + + +--- +doubles: + - skip: + version: " - 7.11.99" + reason: Runtime mappings support was added in 7.12 + + - do: + indices.create: + index: index1 + body: + mappings: + _source: + mode: synthetic + properties: + field1: + type: double + field2: + type: double + + - do: + search: + body: + size: 0 + runtime_mappings: + field3: + type: double + script: + source: "emit(params._source.field1 + params._source.field2)" + aggs: + field3: + terms: + field: field3 + + - length: { aggregations.field3.buckets: 0 } + + - do: + bulk: + index: index1 + refresh: true + body: | + {"index":{}} + { "field1": "1", "field2": "2" } + {"index":{}} + { "field1": "1", "field2": "2" } + {"index":{}} + { "field1": "3", "field2": "4" } + + - do: + search: + body: + size: 0 + runtime_mappings: + field3: + type: double + script: + source: "emit(params._source.field1 + params._source.field2)" + aggs: + field3: + terms: + field: field3 + + - length: { aggregations.field3.buckets: 2 } + - match: { aggregations.field3.buckets.0.key: 3.0 } + - match: { aggregations.field3.buckets.0.doc_count: 2 } + - match: { aggregations.field3.buckets.1.key: 7.0 } + - match: { aggregations.field3.buckets.1.doc_count: 1 } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SortedNumericDocValuesSyntheticFieldLoader.java b/server/src/main/java/org/elasticsearch/index/mapper/SortedNumericDocValuesSyntheticFieldLoader.java index 8ffcdae01a40a..c3ebe079e886e 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SortedNumericDocValuesSyntheticFieldLoader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SortedNumericDocValuesSyntheticFieldLoader.java @@ -60,7 +60,7 @@ public DocValuesLoader docValuesLoader(LeafReader reader, int[] docIdsInLeaf) th values = NO_VALUES; return null; } - if (docIdsInLeaf.length > 1) { + if (docIdsInLeaf != null && docIdsInLeaf.length > 1) { /* * The singleton optimization is mostly about looking up all * values for the field at once. If there's just a single diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SortedSetDocValuesSyntheticFieldLoader.java b/server/src/main/java/org/elasticsearch/index/mapper/SortedSetDocValuesSyntheticFieldLoader.java index 0e30e5a1db7f2..37b6fe72c3089 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SortedSetDocValuesSyntheticFieldLoader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SortedSetDocValuesSyntheticFieldLoader.java @@ -87,7 +87,7 @@ public DocValuesLoader docValuesLoader(LeafReader reader, int[] docIdsInLeaf) th docValues = NO_VALUES; return null; } - if (docIdsInLeaf.length > 1) { + if (docIdsInLeaf != null && docIdsInLeaf.length > 1) { /* * The singleton optimization is mostly about looking up ordinals * in sorted order and doesn't buy anything if there is only a single diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SourceLoader.java b/server/src/main/java/org/elasticsearch/index/mapper/SourceLoader.java index 44c1e25c37b60..607ba2b261f5d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SourceLoader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SourceLoader.java @@ -198,6 +198,8 @@ public void write(XContentBuilder b) {} * Build something to load doc values for this field or return * {@code null} if there are no doc values for this field to * load. + * + * @param docIdsInLeaf can be null. */ DocValuesLoader docValuesLoader(LeafReader leafReader, int[] docIdsInLeaf) throws IOException; diff --git a/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java b/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java index 746ada6fe4dcf..8186c9c2d9a01 100644 --- a/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java +++ b/server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java @@ -396,9 +396,9 @@ public boolean containsBrokenAnalysis(String field) { */ public SearchLookup lookup() { if (this.lookup == null) { - SourceProvider sourceProvider = isSourceSynthetic() ? (ctx, doc) -> { - throw new IllegalArgumentException("Cannot access source from scripts in synthetic mode"); - } : SourceProvider.fromStoredFields(); + SourceProvider sourceProvider = isSourceSynthetic() + ? SourceProvider.fromSyntheticSource(mappingLookup.getMapping()) + : SourceProvider.fromStoredFields(); setLookupProviders(sourceProvider, LeafFieldLookupProvider.fromStoredFields()); } return this.lookup; diff --git a/server/src/main/java/org/elasticsearch/search/lookup/SourceProvider.java b/server/src/main/java/org/elasticsearch/search/lookup/SourceProvider.java index c3897481c2abf..27d48613820cd 100644 --- a/server/src/main/java/org/elasticsearch/search/lookup/SourceProvider.java +++ b/server/src/main/java/org/elasticsearch/search/lookup/SourceProvider.java @@ -10,6 +10,7 @@ import org.apache.lucene.index.LeafReaderContext; import org.elasticsearch.index.fieldvisitor.StoredFieldLoader; +import org.elasticsearch.index.mapper.Mapping; import java.io.IOException; @@ -36,4 +37,15 @@ static SourceProvider fromStoredFields() { return new StoredFieldSourceProvider(storedFieldLoader); } + /** + * A SourceProvider that loads source from synthetic source + * + * The returned SourceProvider is thread-safe across segments, in that it may be + * safely used by a searcher that searches different segments on different threads, + * but it is not safe to use this to access documents from the same segment across + * multiple threads. + */ + static SourceProvider fromSyntheticSource(Mapping mapping) { + return new SyntheticSourceProvider(mapping); + } } diff --git a/server/src/main/java/org/elasticsearch/search/lookup/SyntheticSourceProvider.java b/server/src/main/java/org/elasticsearch/search/lookup/SyntheticSourceProvider.java new file mode 100644 index 0000000000000..74327e16d20ea --- /dev/null +++ b/server/src/main/java/org/elasticsearch/search/lookup/SyntheticSourceProvider.java @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.search.lookup; + +import org.apache.lucene.index.IndexReaderContext; +import org.apache.lucene.index.LeafReaderContext; +import org.elasticsearch.index.fieldvisitor.LeafStoredFieldLoader; +import org.elasticsearch.index.fieldvisitor.StoredFieldLoader; +import org.elasticsearch.index.mapper.Mapping; +import org.elasticsearch.index.mapper.SourceLoader; + +import java.io.IOException; + +// NB This is written under the assumption that individual segments are accessed by a single +// thread, even if separate segments may be searched concurrently. If we ever implement +// within-segment concurrency this will have to work entirely differently. +class SyntheticSourceProvider implements SourceProvider { + + private final SourceLoader sourceLoader; + private volatile SyntheticSourceLeafLoader[] leafLoaders; + + SyntheticSourceProvider(Mapping mapping) { + sourceLoader = new SourceLoader.Synthetic(mapping); + } + + @Override + public Source getSource(LeafReaderContext ctx, int doc) throws IOException { + maybeInit(ctx); + if (leafLoaders[ctx.ord] == null) { + // individual segments are currently only accessed on one thread so there's no need + // for locking here. + leafLoaders[ctx.ord] = new SyntheticSourceLeafLoader(ctx); + } + return leafLoaders[ctx.ord].getSource(doc); + } + + private void maybeInit(LeafReaderContext ctx) { + if (leafLoaders == null) { + synchronized (this) { + if (leafLoaders == null) { + leafLoaders = new SyntheticSourceLeafLoader[findParentContext(ctx).leaves().size()]; + } + } + } + } + + private IndexReaderContext findParentContext(LeafReaderContext ctx) { + if (ctx.parent != null) { + return ctx.parent; + } + assert ctx.isTopLevel; + return ctx; + } + + private class SyntheticSourceLeafLoader { + private final LeafStoredFieldLoader leafLoader; + private final SourceLoader.Leaf leaf; + + SyntheticSourceLeafLoader(LeafReaderContext ctx) throws IOException { + this.leafLoader = (sourceLoader.requiredStoredFields().isEmpty()) + ? StoredFieldLoader.empty().getLoader(ctx, null) + : StoredFieldLoader.create(false, sourceLoader.requiredStoredFields()).getLoader(ctx, null); + this.leaf = sourceLoader.leaf(ctx.reader(), null); + } + + Source getSource(int doc) throws IOException { + leafLoader.advanceTo(doc); + return leaf.source(leafLoader, doc); + } + } +} diff --git a/server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java b/server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java index 5cf329b76bb3f..6d671a258c26a 100644 --- a/server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java @@ -8,10 +8,12 @@ package org.elasticsearch.index.query; import org.apache.lucene.document.Field; +import org.apache.lucene.document.KeywordField; import org.apache.lucene.document.StringField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; +import org.apache.lucene.index.memory.MemoryIndex; import org.apache.lucene.search.Collector; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafCollector; @@ -71,7 +73,6 @@ import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.aggregations.support.ValuesSourceType; import org.elasticsearch.search.lookup.LeafDocLookup; -import org.elasticsearch.search.lookup.LeafFieldLookupProvider; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.Source; @@ -79,7 +80,6 @@ import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentParserConfiguration; -import org.elasticsearch.xcontent.XContentType; import org.mockito.stubbing.Answer; import java.io.IOException; @@ -379,28 +379,26 @@ public void testSearchRequestRuntimeFieldsAndMultifieldDetection() { assertTrue(mappingLookup.isMultiField("cat.subfield")); } - public void testSyntheticSourceScriptLoading() throws IOException { - + public void testSyntheticSourceSearchLookup() throws IOException { // Build a mapping using synthetic source SourceFieldMapper sourceMapper = new SourceFieldMapper.Builder(null).setSynthetic().build(); - RootObjectMapper root = new RootObjectMapper.Builder("_doc", Explicit.IMPLICIT_TRUE).build(MapperBuilderContext.root(true, false)); + RootObjectMapper root = new RootObjectMapper.Builder("_doc", Explicit.IMPLICIT_TRUE).add( + new KeywordFieldMapper.Builder("cat", IndexVersion.current()).ignoreAbove(100) + ).build(MapperBuilderContext.root(true, false)); Mapping mapping = new Mapping(root, new MetadataFieldMapper[] { sourceMapper }, Map.of()); MappingLookup lookup = MappingLookup.fromMapping(mapping); SearchExecutionContext sec = createSearchExecutionContext("index", "", lookup, Map.of()); + assertTrue(sec.isSourceSynthetic()); - // Attempting to access synthetic source via this context should throw an error - SearchLookup searchLookup = sec.lookup(); - Exception e = expectThrows(IllegalArgumentException.class, () -> searchLookup.getSource(null, 0)); - assertThat(e.getMessage(), equalTo("Cannot access source from scripts in synthetic mode")); - - // Setting the source provider explicitly then gives us a new SearchLookup that can use source - Source source = Source.fromMap(Map.of("field", "value"), XContentType.JSON); - sec.setLookupProviders((ctx, doc) -> source, LeafFieldLookupProvider.fromStoredFields()); - SearchLookup searchLookup1 = sec.lookup(); - assertNotSame(searchLookup, searchLookup1); - assertSame(source, searchLookup1.getSource(null, 0)); + MemoryIndex mi = new MemoryIndex(); + mi.addField(new KeywordField("cat", "meow", Field.Store.YES), null); + LeafReaderContext leafReaderContext = mi.createSearcher().getIndexReader().leaves().get(0); + SearchLookup searchLookup = sec.lookup(); + Source source = searchLookup.getSource(leafReaderContext, 0); + assertEquals(1, source.source().size()); + assertEquals("meow", source.source().get("cat")); } public static SearchExecutionContext createSearchExecutionContext(String indexUuid, String clusterAlias) { diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java index 11cfe0150b07a..22821dac1bffb 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java @@ -65,6 +65,7 @@ import org.elasticsearch.search.internal.SubSearchContext; import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.Source; +import org.elasticsearch.search.lookup.SourceProvider; import org.elasticsearch.search.sort.BucketedSort; import org.elasticsearch.search.sort.BucketedSort.ExtraData; import org.elasticsearch.search.sort.SortAndFormats; @@ -719,10 +720,8 @@ private void roundTripSyntheticSource(DocumentMapper mapper, String syntheticSou } private String syntheticSource(DocumentMapper mapper, IndexReader reader, int docId) throws IOException { - SourceLoader loader = mapper.sourceMapper().newSourceLoader(mapper.mapping()); - LeafReader leafReader = getOnlyLeafReader(reader); - SourceLoader.Leaf leafLoader = loader.leaf(leafReader, new int[] { docId }); - Source synthetic = leafLoader.source(syntheticSourceStoredFieldLoader(mapper, leafReader, loader), docId); + SourceProvider provider = SourceProvider.fromSyntheticSource(mapper.mapping()); + Source synthetic = provider.getSource(getOnlyLeafReader(reader).getContext(), docId); return synthetic.internalSourceRef().utf8ToString(); } From 0a91c2476577198bd9a8c1df5bd70b2ab7abfb45 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 26 Sep 2023 14:23:17 +0200 Subject: [PATCH 080/155] Fix limit default size setting key (#99898) Fix c&p error: `s/esql.query.result_truncation_max_size/esql.query.result_truncation_default_size/` --- .../java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java index b4f91ca1ceb11..909c6c12893ab 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java @@ -77,7 +77,7 @@ public class EsqlPlugin extends Plugin implements ActionPlugin { ); public static final Setting QUERY_RESULT_TRUNCATION_DEFAULT_SIZE = Setting.intSetting( - "esql.query.result_truncation_max_size", + "esql.query.result_truncation_default_size", 500, 1, 10000, From 8582e53ef0daac8d4e625706c6b067bd8342c2e6 Mon Sep 17 00:00:00 2001 From: Alexander Spies Date: Tue, 26 Sep 2023 17:39:40 +0200 Subject: [PATCH 081/155] ESQL: Date math for negatives (#99711) Enable expressions like now() + (-(1hour + 1minute)). --- docs/changelog/99711.yaml | 5 +++ .../xpack/esql/qa/rest/RestEsqlTestCase.java | 34 +++++++------- .../src/main/resources/date.csv-spec | 16 +++++++ .../xpack/esql/ExceptionUtils.java | 24 ++++++++++ .../esql/analysis/VerificationException.java | 3 ++ .../DateTimeArithmeticOperation.java | 26 ++--------- .../predicate/operator/arithmetic/Neg.java | 45 ++++++++++--------- .../function/AbstractFunctionTestCase.java | 2 +- .../operator/arithmetic/AddTests.java | 9 ++-- .../operator/arithmetic/NegTests.java | 40 ++++++++++------- 10 files changed, 123 insertions(+), 81 deletions(-) create mode 100644 docs/changelog/99711.yaml create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/ExceptionUtils.java diff --git a/docs/changelog/99711.yaml b/docs/changelog/99711.yaml new file mode 100644 index 0000000000000..34731a52818f0 --- /dev/null +++ b/docs/changelog/99711.yaml @@ -0,0 +1,5 @@ +pr: 99711 +summary: "ESQL: Date math for negatives" +area: ES|QL +type: enhancement +issues: [] diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java index 82c5f65d210ce..2b8bead0f86bc 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java @@ -343,38 +343,40 @@ public void testErrorMessageForInvalidTypeInParams() throws IOException { } public void testErrorMessageForLiteralDateMathOverflow() throws IOException { - List datePeriodOverflowExpressions = List.of( + List dateMathOverflowExpressions = List.of( "2147483647 day + 1 day", "306783378 week + 1 week", - "2147483647 year + 1 year" - ); - // We cannot easily force an overflow using just milliseconds, since these are divided by 1000 and then the resulting seconds are - // stored in a long. But combining with seconds works. - List timeDurationOverflowExpressions = List.of( + "2147483647 month + 1 month", + "2147483647 year + 1 year", + // We cannot easily force an overflow using just milliseconds, since these are divided by 1000 and then the resulting seconds + // are stored in a long. But combining with seconds works. "9223372036854775807 second + 1000 millisecond", "9223372036854775807 second + 1 second", "153722867280912930 minute + 1 minute", "2562047788015215 hour + 1 hour" + ); - for (String overflowExp : datePeriodOverflowExpressions) { - assertDateMathOverflow(overflowExp, "integer overflow"); - } - for (String overflowExp : timeDurationOverflowExpressions) { - assertDateMathOverflow(overflowExp, "long overflow"); + for (String overflowExp : dateMathOverflowExpressions) { + assertExceptionForDateMath(overflowExp, "overflow"); } + + } + + public void testErrorMessageForLiteralDateMathOverflowOnNegation() throws IOException { + assertExceptionForDateMath("-(-2147483647 year - 1 year)", "overflow"); + assertExceptionForDateMath("-(-9223372036854775807 second - 1 second)", "Exceeds capacity of Duration"); } - private static void assertDateMathOverflow(String overflowExpression, String expectedOverflowMessage) throws IOException { + private static void assertExceptionForDateMath(String dateMathString, String errorSubstring) throws IOException { ResponseException re = expectThrows( ResponseException.class, - () -> runEsql(new RequestObjectBuilder().query("row a = 1 | eval x = now() + (" + overflowExpression + ")").build()) + () -> runEsql(new RequestObjectBuilder().query("row a = 1 | eval x = now() + (" + dateMathString + ")").build()) ); String responseMessage = EntityUtils.toString(re.getResponse().getEntity()); - assertThat(responseMessage, containsString("arithmetic exception in expression [" + overflowExpression + "]:")); - // The second part of the error message might come after a newline, so we check for it separately. - assertThat(responseMessage, containsString("[" + expectedOverflowMessage + "]")); + // the error in the response message might be chopped up by newlines, but finding "overflow" should suffice. + assertThat(responseMessage, containsString(errorSubstring)); assertThat(re.getResponse().getStatusLine().getStatusCode(), equalTo(400)); } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/date.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/date.csv-spec index 5adecec275682..3ab423e8d721d 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/date.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/date.csv-spec @@ -488,6 +488,14 @@ then:datetime 1957-07-19T00:00:00.000Z ; +datePlusNegatedPeriod +row dt = to_dt("2104-04-16T01:01:01.000Z") +| eval plus = dt + (-(4 years + 3 months + 2 weeks + 1 day)); + +dt:datetime |plus:datetime +2104-04-16T01:01:01.000Z |2100-01-01T01:01:01.000Z +; + dateMinusPeriod row dt = to_dt("2104-04-16T01:01:01.000Z") | eval minus = dt - 4 years - 3 months - 2 weeks - 1 day; @@ -551,6 +559,14 @@ then:datetime 1953-04-04T01:01:01.001Z ; +datePlusNegatedDuration +row dt = to_dt("2100-01-01T01:01:01.001Z") +| eval plus = dt + (-(1 hour + 1 minute + 1 second + 1 milliseconds)); + +dt:datetime |plus:datetime +2100-01-01T01:01:01.001Z |2100-01-01T00:00:00.000Z +; + dateMinusDuration row dt = to_dt("2100-01-01T01:01:01.001Z") | eval minus = dt - 1 hour - 1 minute - 1 second - 1 milliseconds; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/ExceptionUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/ExceptionUtils.java new file mode 100644 index 0000000000000..79e82092e5b79 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/ExceptionUtils.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql; + +import org.elasticsearch.xpack.esql.analysis.VerificationException; +import org.elasticsearch.xpack.ql.tree.Source; + +public class ExceptionUtils { + /** + * Create a {@link VerificationException} from an {@link ArithmeticException} thrown because of an invalid math expression. + * + * @param source the invalid part of the query causing the exception + * @param e the exception that was thrown + * @return an exception with a user-readable error message with http code 400 + */ + public static VerificationException math(Source source, ArithmeticException e) { + return new VerificationException("arithmetic exception in expression [{}]: [{}]", source.text(), e.getMessage()); + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/VerificationException.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/VerificationException.java index 972ebb40cb8bc..4a86dd1741daa 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/VerificationException.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/VerificationException.java @@ -14,6 +14,9 @@ import java.util.Collection; public class VerificationException extends EsqlClientException { + public VerificationException(String message, Object... args) { + super(message, args); + } protected VerificationException(Collection sources) { super(Failure.failMessage(sources)); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java index 19552d4e873cd..cc4cf1c9aec04 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java @@ -9,8 +9,7 @@ import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.esql.EsqlClientException; +import org.elasticsearch.xpack.esql.ExceptionUtils; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.Source; @@ -29,25 +28,6 @@ import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.isTemporalAmount; abstract class DateTimeArithmeticOperation extends EsqlArithmeticOperation { - - /** - * Custom exception to handle e.g. overflows when folding temporal values; we want to set the correct HTTP status (400). - */ - private static class IllegalTemporalValueException extends EsqlClientException { - protected IllegalTemporalValueException(String message, Object... args) { - super(message, args); - } - - @Override - public RestStatus status() { - return RestStatus.BAD_REQUEST; - } - - public static IllegalTemporalValueException fromArithmeticException(Source source, ArithmeticException e) { - return new IllegalTemporalValueException("arithmetic exception in expression [{}]: [{}]", source.text(), e.getMessage()); - } - } - /** Arithmetic (quad) function. */ interface DatetimeArithmeticEvaluator { ExpressionEvaluator apply( @@ -133,7 +113,7 @@ public final Object fold() { } catch (ArithmeticException e) { // Folding will be triggered before the plan is sent to the compute service, so we have to handle arithmetic exceptions // manually and provide a user-friendly error message. - throw IllegalTemporalValueException.fromArithmeticException(source(), e); + throw ExceptionUtils.math(source(), e); } } if (leftDataType == TIME_DURATION && rightDataType == TIME_DURATION) { @@ -145,7 +125,7 @@ public final Object fold() { } catch (ArithmeticException e) { // Folding will be triggered before the plan is sent to the compute service, so we have to handle arithmetic exceptions // manually and provide a user-friendly error message. - throw IllegalTemporalValueException.fromArithmeticException(source(), e); + throw ExceptionUtils.math(source(), e); } } return super.fold(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java index 0ce4b1bad6a37..97a0323829d59 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java @@ -10,11 +10,11 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.ExceptionUtils; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.Warnings; import org.elasticsearch.xpack.esql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.ql.expression.Expression; -import org.elasticsearch.xpack.ql.expression.Literal; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; @@ -25,6 +25,8 @@ import java.util.List; import java.util.function.Function; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.DATE_PERIOD; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.TIME_DURATION; import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.isTemporalAmount; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.ParamOrdinal.DEFAULT; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isType; @@ -67,29 +69,30 @@ else if (type == DataTypes.LONG) { @Override public final Object fold() { - if (isTemporalAmount(field().dataType()) && field() instanceof Literal literal) { - return foldTemporalAmount(literal); - } - return EvaluatorMapper.super.fold(); - } - - private Object foldTemporalAmount(Literal literal) { - try { - Object value = literal.fold(); - if (value instanceof Period period) { - return period.negated(); + DataType dataType = field().dataType(); + // For date periods and time durations, we need to treat folding differently. These types are unrepresentable, so there is no + // evaluator for them - but the default folding requires an evaluator. + if (dataType == DATE_PERIOD) { + Period fieldValue = (Period) field().fold(); + try { + return fieldValue.negated(); + } catch (ArithmeticException e) { + // Folding will be triggered before the plan is sent to the compute service, so we have to handle arithmetic exceptions + // manually and provide a user-friendly error message. + throw ExceptionUtils.math(source(), e); } - if (value instanceof Duration duration) { - return duration.negated(); + } + if (dataType == TIME_DURATION) { + Duration fieldValue = (Duration) field().fold(); + try { + return fieldValue.negated(); + } catch (ArithmeticException e) { + // Folding will be triggered before the plan is sent to the compute service, so we have to handle arithmetic exceptions + // manually and provide a user-friendly error message. + throw ExceptionUtils.math(source(), e); } - } catch (ArithmeticException ae) { - warnings.registerException(ae); - return null; } - - throw new EsqlIllegalArgumentException( - "unexpected non-temporal amount literal [" + literal.sourceText() + "] of type [" + literal.dataType() + "]" - ); + return EvaluatorMapper.super.fold(); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 8f19ba7cbb584..a190eb867ff63 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -96,7 +96,7 @@ public static Literal randomLiteral(DataType type) { case "half_float" -> HalfFloatPoint.sortableShortToHalfFloat(HalfFloatPoint.halfFloatToSortableShort(randomFloat())); case "keyword" -> new BytesRef(randomAlphaOfLength(5)); case "ip" -> new BytesRef(InetAddressPoint.encode(randomIp(randomBoolean()))); - case "time_duration" -> Duration.ofNanos(randomLongBetween(-604800000000000L, 604800000000000L)); + case "time_duration" -> Duration.ofMillis(randomLongBetween(-604800000L, 604800000L)); // plus/minus 7 days case "text" -> new BytesRef(randomAlphaOfLength(50)); case "version" -> new Version(randomIdentifier()).toBytesRef(); case "null" -> null; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddTests.java index 454c8d2ae5a6e..78298f4480951 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddTests.java @@ -99,7 +99,8 @@ public static Iterable parameters() { new TestCaseSupplier.TypedData(lhs, DataTypes.DATETIME, "lhs"), new TestCaseSupplier.TypedData(rhs, EsqlDataTypes.DATE_PERIOD, "rhs") ), - "AddDatetimesEvaluator[lhs=Attribute[channel=0], rhs=Attribute[channel=1]]", + // TODO: There is an evaluator for Datetime + Period, so it should be tested. Similarly below. + "No evaluator, the tests only trigger the folding code since Period is not representable", DataTypes.DATETIME, equalTo(asMillis(asDateTime(lhs).plus(rhs))) ); @@ -111,7 +112,7 @@ public static Iterable parameters() { new TestCaseSupplier.TypedData(lhs, EsqlDataTypes.DATE_PERIOD, "lhs"), new TestCaseSupplier.TypedData(rhs, DataTypes.DATETIME, "rhs") ), - "AddDatetimesEvaluator[lhs=Attribute[channel=0], rhs=Attribute[channel=1]]", + "No evaluator, the tests only trigger the folding code since Period is not representable", DataTypes.DATETIME, equalTo(asMillis(asDateTime(rhs).plus(lhs))) ); @@ -135,7 +136,7 @@ public static Iterable parameters() { new TestCaseSupplier.TypedData(lhs, DataTypes.DATETIME, "lhs"), new TestCaseSupplier.TypedData(rhs, EsqlDataTypes.TIME_DURATION, "rhs") ), - "AddDatetimesEvaluator[lhs=Attribute[channel=0], rhs=Attribute[channel=1]]", + "No evaluator, the tests only trigger the folding code since Duration is not representable", DataTypes.DATETIME, equalTo(asMillis(asDateTime(lhs).plus(rhs))) ); @@ -147,7 +148,7 @@ public static Iterable parameters() { new TestCaseSupplier.TypedData(lhs, DataTypes.DATETIME, "lhs"), new TestCaseSupplier.TypedData(rhs, EsqlDataTypes.TIME_DURATION, "rhs") ), - "AddDatetimesEvaluator[lhs=Attribute[channel=0], rhs=Attribute[channel=1]]", + "No evaluator, the tests only trigger the folding code since Duration is not representable", DataTypes.DATETIME, equalTo(asMillis(asDateTime(lhs).plus(rhs))) ); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegTests.java index 0138160ebd0fc..ad2cabb35cc8e 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegTests.java @@ -10,6 +10,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.xpack.esql.analysis.VerificationException; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractScalarFunctionTestCase; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -67,7 +68,7 @@ public static Iterable parameters() { Duration arg = (Duration) randomLiteral(EsqlDataTypes.TIME_DURATION).value(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(arg, EsqlDataTypes.TIME_DURATION, "arg")), - "NegDurationEvaluator[v=Attribute[channel=0]]", + "No evaluator since this expression is only folded", EsqlDataTypes.TIME_DURATION, equalTo(arg.negated()) ); @@ -75,7 +76,7 @@ public static Iterable parameters() { Period arg = (Period) randomLiteral(EsqlDataTypes.DATE_PERIOD).value(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(arg, EsqlDataTypes.DATE_PERIOD, "arg")), - "NegPeriodEvaluator[v=Attribute[channel=0]]", + "No evaluator since this expression is only folded", EsqlDataTypes.DATE_PERIOD, equalTo(arg.negated()) ); @@ -139,32 +140,39 @@ public void testEdgeCases() { return; } if (testCaseType == EsqlDataTypes.DATE_PERIOD) { - Period minPeriod = Period.of(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); - assertNull(process(minPeriod)); - assertCriticalWarnings( - "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.", - "java.lang.ArithmeticException: integer overflow" - ); - Period maxPeriod = Period.of(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); Period negatedMaxPeriod = Period.of(-Integer.MAX_VALUE, -Integer.MAX_VALUE, -Integer.MAX_VALUE); assertEquals(negatedMaxPeriod, process(maxPeriod)); + + Period minPeriod = Period.of(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + VerificationException e = expectThrows( + VerificationException.class, + "Expected exception when negating minimal date period.", + () -> process(minPeriod) + ); + assertEquals(e.getMessage(), "arithmetic exception in expression []: [integer overflow]"); + return; } if (testCaseType == EsqlDataTypes.TIME_DURATION) { - Duration minDuration = Duration.ofSeconds(Long.MIN_VALUE, 0); - assertNull(process(minDuration)); - assertCriticalWarnings( - "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.", - "java.lang.ArithmeticException: Exceeds capacity of Duration: 9223372036854775808000000000" - ); - Duration maxDuration = Duration.ofSeconds(Long.MAX_VALUE, 0); Duration negatedMaxDuration = Duration.ofSeconds(-Long.MAX_VALUE, 0); assertEquals(negatedMaxDuration, process(maxDuration)); + Duration minDuration = Duration.ofSeconds(Long.MIN_VALUE, 0); + VerificationException e = expectThrows( + VerificationException.class, + "Expected exception when negating minimal time duration.", + () -> process(minDuration) + ); + assertEquals( + e.getMessage(), + "arithmetic exception in expression []: [Exceeds capacity of Duration: 9223372036854775808000000000]" + ); + return; } + throw new AssertionError("Edge cases not tested for negation with type [" + testCaseType.typeName() + "]"); } From cc42ff62b30b266dd715ae6ab203b790dfc8fe23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 26 Sep 2023 17:51:51 +0200 Subject: [PATCH 082/155] Fix fields API for geo_point fields inside other arrays (#99868) The fields API currently doesn't work for geopoint arrays inside outer objects that again are expressed as multi-values arrays. The reason is that upon extracting the leaf values from source (i.e via Source#extractValue) we accumulate intermediate array structures as additinal lists in the source value we then try to parse. For other field types, especially those that use SourceValueFetcher, this doesn't matter because we have a list-flattening step in SourceValueFetcher#fetchValues, but for geo_point types the parser cannot deal with the additional list levels. This change modifies the source extraction so that we avoid nesting to many list structures inside each other, which I believe currently is wrong but is masked by the list deduplication mentioned above. Closes #99781 --- docs/changelog/99868.yaml | 6 + .../xcontent/support/XContentMapValues.java | 13 +- .../support/XContentMapValuesTests.java | 90 ++++++--- .../fetch/subphase/FieldFetcherTests.java | 177 ++++++++++++++++++ 4 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 docs/changelog/99868.yaml diff --git a/docs/changelog/99868.yaml b/docs/changelog/99868.yaml new file mode 100644 index 0000000000000..33d582f9ebd0a --- /dev/null +++ b/docs/changelog/99868.yaml @@ -0,0 +1,6 @@ +pr: 99868 +summary: Fix fields API for `geo_point` fields inside other arrays +area: Search +type: bug +issues: + - 99781 diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java b/server/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java index 1b9329eae1ce4..805931550ad62 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java @@ -195,7 +195,18 @@ private static Object extractValue(String[] pathElements, int index, Object curr for (Object o : valueList) { Object listValue = extractValue(pathElements, index, o, nullValue); if (listValue != null) { - newList.add(listValue); + // we add arrays as list elements only if we are already at leaf, + // otherwise append individual elements to the new list so we don't + // accumulate intermediate array structures + if (listValue instanceof List list) { + if (index == pathElements.length) { + newList.add(list); + } else { + newList.addAll(list); + } + } else { + newList.add(listValue); + } } } return newList; diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java b/server/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java index cd3470d125889..ca2a99b842cd9 100644 --- a/server/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java +++ b/server/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java @@ -72,10 +72,7 @@ protected void testFilter(Builder expected, Builder actual, Collection i public void testExtractValue() throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder().startObject().field("test", "value").endObject(); - Map map; - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + Map map = toSourceMap(Strings.toString(builder)); assertThat(XContentMapValues.extractValue("test", map).toString(), equalTo("value")); assertThat(XContentMapValues.extractValue("test.me", map), nullValue()); assertThat(XContentMapValues.extractValue("something.else.2", map), nullValue()); @@ -84,9 +81,7 @@ public void testExtractValue() throws Exception { builder.startObject("path1").startObject("path2").field("test", "value").endObject().endObject(); builder.endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + map = toSourceMap(Strings.toString(builder)); assertThat(XContentMapValues.extractValue("path1.path2.test", map).toString(), equalTo("value")); assertThat(XContentMapValues.extractValue("path1.path2.test_me", map), nullValue()); assertThat(XContentMapValues.extractValue("path1.non_path2.test", map), nullValue()); @@ -105,10 +100,7 @@ public void testExtractValue() throws Exception { builder = XContentFactory.jsonBuilder().startObject(); builder.startObject("path1").array("test", "value1", "value2").endObject(); builder.endObject(); - - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + map = toSourceMap(Strings.toString(builder)); extValue = XContentMapValues.extractValue("path1.test", map); assertThat(extValue, instanceOf(List.class)); @@ -128,10 +120,7 @@ public void testExtractValue() throws Exception { builder.endObject(); } builder.endObject(); - - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + map = toSourceMap(Strings.toString(builder)); extValue = XContentMapValues.extractValue("path1.path2.test", map); assertThat(extValue, instanceOf(List.class)); @@ -143,19 +132,78 @@ public void testExtractValue() throws Exception { // fields with . in them builder = XContentFactory.jsonBuilder().startObject().field("xxx.yyy", "value").endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + map = toSourceMap(Strings.toString(builder)); assertThat(XContentMapValues.extractValue("xxx.yyy", map).toString(), equalTo("value")); builder = XContentFactory.jsonBuilder().startObject(); builder.startObject("path1.xxx").startObject("path2.yyy").field("test", "value").endObject().endObject(); builder.endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { - map = parser.map(); - } + map = toSourceMap(Strings.toString(builder)); assertThat(XContentMapValues.extractValue("path1.xxx.path2.yyy.test", map).toString(), equalTo("value")); + + String source = """ + { + "object" : [ + { + "object2" : [ + { + "foo" : [1,2,3], + "bar" : "baz" + }, + { + "bar" : ["buzz", "bees"] + } + ], + "geo_point_in_obj" : [ + {"lat" : 42.0, "lon" : 27.1}, + [2.1, 41.0] + ] + } + ] + } + """; + + assertThat( + XContentMapValues.extractValue("object.geo_point_in_obj", toSourceMap(source)).toString(), + equalTo("[{lon=27.1, lat=42.0}, [2.1, 41.0]]") + ); + assertThat(XContentMapValues.extractValue("object.object2.foo", toSourceMap(source)).toString(), equalTo("[1, 2, 3]")); + assertThat(XContentMapValues.extractValue("object.object2.bar", toSourceMap(source)).toString(), equalTo("[baz, buzz, bees]")); + + // same with the root object not being an array + source = """ + { + "object" : { + "object2" : [ + { + "foo" : [1,2,3], + "bar" : "baz" + }, + { + "bar" : ["buzz", "bees"] + } + ], + "geo_point_in_obj" : [ + {"lat" : 42.0, "lon" : 27.1}, + [2.1, 41.0] + ] + } + } + """; + + assertThat( + XContentMapValues.extractValue("object.geo_point_in_obj", toSourceMap(source)).toString(), + equalTo("[{lon=27.1, lat=42.0}, [2.1, 41.0]]") + ); + assertThat(XContentMapValues.extractValue("object.object2.foo", toSourceMap(source)).toString(), equalTo("[1, 2, 3]")); + assertThat(XContentMapValues.extractValue("object.object2.bar", toSourceMap(source)).toString(), equalTo("[baz, buzz, bees]")); + } + + private Map toSourceMap(String source) throws IOException { + try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) { + return parser.map(); + } } public void testExtractValueWithNullValue() throws Exception { diff --git a/server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldFetcherTests.java b/server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldFetcherTests.java index acd69788737d2..7a1751dbd41fc 100644 --- a/server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldFetcherTests.java +++ b/server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldFetcherTests.java @@ -394,6 +394,176 @@ public void testArrayValueMappers() throws IOException { assertThat(field.getValues().size(), equalTo(2)); } + public void testGeopointArrayInObject() throws IOException { + MapperService mapperService = createMapperService(); + { + String source = """ + { + "object" : [ + { + "geo_point_in_obj" : [ + {"lat" : 42.0, "lon" : 27.1}, + [2.1, 41.0] + ] + } + ] + } + """; + + Map fields = fetchFields( + mapperService, + source, + fieldAndFormatList("object.geo_point_in_obj", null, false) + ); + assertThat(fields.size(), equalTo(1)); + + DocumentField field = fields.get("object.geo_point_in_obj"); + assertNotNull(field); + List values = field.getValues(); + assertThat(values.size(), equalTo(2)); + assertPoint((Map) values.get(0), 42.0, 27.1); + assertPoint((Map) values.get(1), 41.0, 2.1); + } + { + // check the same without the root field as array + String source = """ + { + "object" : { + "geo_point_in_obj" : [ + {"lat" : 42.0, "lon" : 27.1}, + [2.1, 41.0] + ] + } + } + """; + + Map fields = fetchFields( + mapperService, + source, + fieldAndFormatList("object.geo_point_in_obj", null, false) + ); + assertThat(fields.size(), equalTo(1)); + + DocumentField field = fields.get("object.geo_point_in_obj"); + assertNotNull(field); + List values = field.getValues(); + assertThat(values.size(), equalTo(2)); + assertPoint((Map) values.get(0), 42.0, 27.1); + assertPoint((Map) values.get(1), 41.0, 2.1); + } + } + + private void assertPoint(Map pointMap, double lat, double lon) { + assertEquals("Point", pointMap.get("type")); + assertEquals(List.of(lon, lat), pointMap.get("coordinates")); + } + + public void testDenseVectorInObject() throws IOException { + MapperService mapperService = createMapperService(); + { + String source = """ + { + "object" : [ + { + "dense_vector_in_obj" : [ 1, 2, 3] + } + ] + } + """; + + Map fields = fetchFields( + mapperService, + source, + fieldAndFormatList("object.dense_vector_in_obj", null, false) + ); + assertThat(fields.size(), equalTo(1)); + + DocumentField field = fields.get("object.dense_vector_in_obj"); + assertNotNull(field); + List values = field.getValues(); + assertThat(field.getValues().size(), equalTo(3)); + } + { + // check the same without the root field as array + String source = """ + { + "object" : { + "dense_vector_in_obj" : [ 1, 2, 3] + } + } + """; + + Map fields = fetchFields( + mapperService, + source, + fieldAndFormatList("object.dense_vector_in_obj", null, false) + ); + assertThat(fields.size(), equalTo(1)); + + DocumentField field = fields.get("object.dense_vector_in_obj"); + assertNotNull(field); + List values = field.getValues(); + assertThat(values.size(), equalTo(3)); + } + } + + public void testKeywordArrayInObject() throws IOException { + MapperService mapperService = createMapperService(); + + String source = """ + { + "object" : [ + { + "field" : [ "foo", "bar"] + } + ] + } + """; + + Map fields = fetchFields(mapperService, source, fieldAndFormatList("object.field", null, false)); + assertThat(fields.size(), equalTo(1)); + + DocumentField field = fields.get("object.field"); + assertNotNull(field); + assertThat(field.getValues().size(), equalTo(2)); + + source = """ + { + "object" : { + "field" : [ "foo", "bar", "baz"] + } + } + """; + + fields = fetchFields(mapperService, source, fieldAndFormatList("object.field", null, false)); + assertThat(fields.size(), equalTo(1)); + + field = fields.get("object.field"); + assertNotNull(field); + assertThat(field.getValues().size(), equalTo(3)); + + // mixing array and singleton object on two separate paths + source = """ + { + "object" : [ + { + "field" : "foo" + }, + { + "field" : [ "bar", "baz"] + } + ] + } + """; + + fields = fetchFields(mapperService, source, fieldAndFormatList("object.field", null, false)); + assertThat(fields.size(), equalTo(1)); + + field = fields.get("object.field"); + assertNotNull(field); + assertThat(field.getValues(), containsInAnyOrder("foo", "bar", "baz")); + } + public void testFieldNamesWithWildcard() throws IOException { MapperService mapperService = createMapperService(); XContentBuilder source = XContentFactory.jsonBuilder() @@ -1467,6 +1637,13 @@ public MapperService createMapperService() throws IOException { .startObject("field") .field("type", "keyword") .endObject() + .startObject("geo_point_in_obj") + .field("type", "geo_point") + .endObject() + .startObject("dense_vector_in_obj") + .field("type", "dense_vector") + .field("dims", 3) + .endObject() .endObject() .endObject() .startObject("field_that_does_not_match") From f77c16b78bab5e9f3ee24974aeba408e254e8c94 Mon Sep 17 00:00:00 2001 From: Dianna Hohensee Date: Tue, 26 Sep 2023 12:58:05 -0400 Subject: [PATCH 083/155] Pass Executor into TransportMasterNodeAction (#99787) Related to #97879 --- .../CreateDataStreamTransportAction.java | 3 +- .../DeleteDataStreamTransportAction.java | 3 +- .../action/GetDataStreamsTransportAction.java | 3 +- .../MigrateToDataStreamTransportAction.java | 3 +- .../ModifyDataStreamsTransportAction.java | 3 +- .../PromoteDataStreamTransportAction.java | 3 +- ...nsportDeleteDataStreamLifecycleAction.java | 3 +- ...sportExplainDataStreamLifecycleAction.java | 2 +- ...TransportGetDataStreamLifecycleAction.java | 3 +- ...TransportPutDataStreamLifecycleAction.java | 3 +- ...ansportClusterAllocationExplainAction.java | 2 +- .../TransportDeleteDesiredBalanceAction.java | 2 +- .../TransportGetDesiredBalanceAction.java | 2 +- ...nsportAddVotingConfigExclusionsAction.java | 3 +- ...portClearVotingConfigExclusionsAction.java | 3 +- .../TransportDeleteDesiredNodesAction.java | 3 +- .../TransportGetDesiredNodesAction.java | 3 +- .../TransportUpdateDesiredNodesAction.java | 3 +- .../health/TransportClusterHealthAction.java | 3 +- ...ransportGetFeatureUpgradeStatusAction.java | 3 +- .../TransportPostFeatureUpgradeAction.java | 3 +- ...TransportPrevalidateNodeRemovalAction.java | 3 +- .../TransportCleanupRepositoryAction.java | 3 +- .../TransportDeleteRepositoryAction.java | 3 +- .../get/TransportGetRepositoriesAction.java | 3 +- .../put/TransportPutRepositoryAction.java | 3 +- .../TransportVerifyRepositoryAction.java | 3 +- .../TransportClusterRerouteAction.java | 3 +- .../TransportClusterGetSettingsAction.java | 3 +- .../TransportClusterUpdateSettingsAction.java | 3 +- .../TransportClusterSearchShardsAction.java | 2 +- .../clone/TransportCloneSnapshotAction.java | 3 +- .../create/TransportCreateSnapshotAction.java | 3 +- .../delete/TransportDeleteSnapshotAction.java | 3 +- .../TransportResetFeatureStateAction.java | 3 +- .../TransportSnapshottableFeaturesAction.java | 3 +- .../get/TransportGetSnapshotsAction.java | 11 +++--- .../TransportGetShardSnapshotAction.java | 3 +- .../TransportRestoreSnapshotAction.java | 3 +- .../TransportSnapshotsStatusAction.java | 3 +- .../state/TransportClusterStateAction.java | 2 +- .../TransportDeleteStoredScriptAction.java | 3 +- .../TransportGetStoredScriptAction.java | 3 +- .../TransportPutStoredScriptAction.java | 3 +- .../TransportPendingClusterTasksAction.java | 3 +- .../alias/TransportIndicesAliasesAction.java | 3 +- .../alias/get/TransportGetAliasesAction.java | 2 +- .../close/TransportCloseIndexAction.java | 3 +- .../indices/create/AutoCreateAction.java | 3 +- .../create/TransportCreateIndexAction.java | 3 +- .../TransportDeleteDanglingIndexAction.java | 2 +- .../delete/TransportDeleteIndexAction.java | 3 +- .../indices/flush/TransportFlushAction.java | 3 +- .../put/TransportAutoPutMappingAction.java | 3 +- .../put/TransportPutMappingAction.java | 3 +- .../open/TransportOpenIndexAction.java | 3 +- .../TransportAddIndexBlockAction.java | 3 +- .../refresh/TransportRefreshAction.java | 2 +- .../rollover/TransportRolloverAction.java | 3 +- .../get/TransportGetSettingsAction.java | 2 +- .../put/TransportUpdateSettingsAction.java | 3 +- .../TransportIndicesShardStoresAction.java | 3 +- .../indices/shrink/TransportResizeAction.java | 3 +- ...ransportDeleteComponentTemplateAction.java | 3 +- ...rtDeleteComposableIndexTemplateAction.java | 3 +- .../TransportDeleteIndexTemplateAction.java | 3 +- .../TransportGetComponentTemplateAction.java | 3 +- ...sportGetComposableIndexTemplateAction.java | 3 +- .../get/TransportGetIndexTemplatesAction.java | 3 +- .../TransportSimulateIndexTemplateAction.java | 3 +- .../post/TransportSimulateTemplateAction.java | 3 +- .../TransportPutComponentTemplateAction.java | 3 +- ...sportPutComposableIndexTemplateAction.java | 3 +- .../put/TransportPutIndexTemplateAction.java | 3 +- .../ingest/DeletePipelineTransportAction.java | 3 +- .../ingest/GetPipelineTransportAction.java | 3 +- .../ingest/PutPipelineTransportAction.java | 3 +- ...AcknowledgedTransportMasterNodeAction.java | 6 ++- .../master/TransportMasterNodeAction.java | 31 ++++++++++++++- .../master/TransportMasterNodeReadAction.java | 6 ++- .../info/TransportClusterInfoAction.java | 2 +- .../TransportBroadcastReplicationAction.java | 7 ++-- .../node/FetchHealthInfoCacheAction.java | 2 +- .../node/UpdateHealthInfoCacheAction.java | 2 +- .../action/TransportHealthNodeAction.java | 8 ++-- .../CompletionPersistentTaskAction.java | 2 +- .../RemovePersistentTaskAction.java | 2 +- .../persistent/StartPersistentTaskAction.java | 2 +- .../UpdatePersistentTaskStatusAction.java | 2 +- .../snapshots/SnapshotsService.java | 3 +- .../TransportRolloverActionTests.java | 8 +--- .../TransportMasterNodeActionTests.java | 38 ++++++++++++++----- .../BroadcastReplicationTests.java | 3 +- .../TransportHealthNodeActionTests.java | 8 ++-- .../InternalOrPrivateSettingsPlugin.java | 3 +- ...ransportDeleteAutoscalingPolicyAction.java | 3 +- ...TransportGetAutoscalingCapacityAction.java | 3 +- .../TransportGetAutoscalingPolicyAction.java | 3 +- .../TransportPutAutoscalingPolicyAction.java | 3 +- ...nsportActivateAutoFollowPatternAction.java | 3 +- .../ccr/action/TransportCcrStatsAction.java | 2 +- ...ransportDeleteAutoFollowPatternAction.java | 3 +- .../ccr/action/TransportFollowInfoAction.java | 3 +- .../TransportGetAutoFollowPatternAction.java | 3 +- .../action/TransportPauseFollowAction.java | 3 +- .../TransportPutAutoFollowPatternAction.java | 3 +- .../ccr/action/TransportPutFollowAction.java | 3 +- .../action/TransportResumeFollowAction.java | 3 +- .../ccr/action/TransportUnfollowAction.java | 3 +- .../license/TransportDeleteLicenseAction.java | 2 +- .../TransportGetBasicStatusAction.java | 3 +- .../license/TransportGetLicenseAction.java | 3 +- .../TransportGetTrialStatusAction.java | 3 +- .../TransportPostStartBasicAction.java | 3 +- .../TransportPostStartTrialAction.java | 3 +- .../license/TransportPutLicenseAction.java | 2 +- .../AbstractTransportSetResetModeAction.java | 3 +- .../action/TransportXPackUsageAction.java | 2 +- .../XPackUsageFeatureTransportAction.java | 2 +- .../TransportDeprecationInfoAction.java | 2 +- .../downsample/TransportDownsampleAction.java | 3 +- .../TransportDeleteEnrichPolicyAction.java | 3 +- .../action/TransportEnrichStatsAction.java | 3 +- .../TransportExecuteEnrichPolicyAction.java | 3 +- .../TransportGetEnrichPolicyAction.java | 3 +- .../TransportPutEnrichPolicyAction.java | 3 +- ...nsportDeleteAnalyticsCollectionAction.java | 3 +- ...TransportGetAnalyticsCollectionAction.java | 3 +- ...TransportPutAnalyticsCollectionAction.java | 3 +- .../action/TransportFreezeIndexAction.java | 3 +- .../TransportDeleteLifecycleAction.java | 3 +- .../action/TransportGetLifecycleAction.java | 2 +- .../ilm/action/TransportGetStatusAction.java | 3 +- .../TransportMigrateToDataTiersAction.java | 3 +- .../ilm/action/TransportMoveToStepAction.java | 3 +- .../action/TransportPutLifecycleAction.java | 3 +- ...sportRemoveIndexLifecyclePolicyAction.java | 3 +- .../ilm/action/TransportRetryAction.java | 3 +- .../ilm/action/TransportStartILMAction.java | 3 +- .../ilm/action/TransportStopILMAction.java | 3 +- .../TransportDeleteInferenceModelAction.java | 3 +- .../TransportPutInferenceModelAction.java | 3 +- ...ortGetTrainedModelPackageConfigAction.java | 3 +- .../TransportLoadTrainedModelPackage.java | 3 +- ...ortCreateTrainedModelAssignmentAction.java | 3 +- ...ansportDeleteDataFrameAnalyticsAction.java | 3 +- .../action/TransportDeleteDatafeedAction.java | 3 +- .../ml/action/TransportDeleteJobAction.java | 3 +- .../TransportDeleteTrainedModelAction.java | 3 +- ...ransportDeleteTrainedModelAliasAction.java | 3 +- ...ortDeleteTrainedModelAssignmentAction.java | 3 +- .../TransportFinalizeJobExecutionAction.java | 3 +- .../action/TransportGetDatafeedsAction.java | 3 +- ...etJobModelSnapshotsUpgradeStatsAction.java | 3 +- .../ml/action/TransportGetJobsAction.java | 3 +- .../TransportGetMlAutoscalingStats.java | 3 +- .../ml/action/TransportMlMemoryAction.java | 3 +- .../ml/action/TransportOpenJobAction.java | 3 +- .../TransportPutDataFrameAnalyticsAction.java | 3 +- .../ml/action/TransportPutDatafeedAction.java | 3 +- .../ml/action/TransportPutJobAction.java | 3 +- .../TransportPutTrainedModelAction.java | 3 +- .../TransportPutTrainedModelAliasAction.java | 3 +- ...nsportPutTrainedModelVocabularyAction.java | 3 +- .../ml/action/TransportResetJobAction.java | 3 +- .../TransportRevertModelSnapshotAction.java | 3 +- .../action/TransportSetUpgradeModeAction.java | 3 +- ...ransportStartDataFrameAnalyticsAction.java | 3 +- .../action/TransportStartDatafeedAction.java | 3 +- ...portStartTrainedModelDeploymentAction.java | 3 +- ...ansportUpdateDataFrameAnalyticsAction.java | 3 +- .../action/TransportUpdateDatafeedAction.java | 3 +- .../ml/action/TransportUpdateJobAction.java | 3 +- ...dateTrainedModelAssignmentStateAction.java | 3 +- ...ortUpdateTrainedModelDeploymentAction.java | 3 +- ...ransportUpgradeJobModelSnapshotAction.java | 3 +- ...nsportFinalizeJobExecutionActionTests.java | 6 +-- ...ransportMonitoringMigrateAlertsAction.java | 2 +- .../profiling/TransportGetStatusAction.java | 3 +- .../action/TransportPutRollupJobAction.java | 3 +- ...ransportMountSearchableSnapshotAction.java | 2 +- .../TransportGetSecuritySettingsAction.java | 3 +- ...TransportUpdateSecuritySettingsAction.java | 3 +- .../TransportDeleteShutdownNodeAction.java | 3 +- .../TransportGetShutdownStatusAction.java | 2 +- .../TransportPutShutdownNodeAction.java | 3 +- ...ransportDeleteSnapshotLifecycleAction.java | 3 +- ...ansportExecuteSnapshotLifecycleAction.java | 2 +- ...ansportExecuteSnapshotRetentionAction.java | 2 +- .../action/TransportGetSLMStatusAction.java | 3 +- .../TransportGetSnapshotLifecycleAction.java | 3 +- ...nsportGetSnapshotLifecycleStatsAction.java | 3 +- .../TransportPutSnapshotLifecycleAction.java | 3 +- .../slm/action/TransportStartSLMAction.java | 3 +- .../slm/action/TransportStopSLMAction.java | 3 +- .../TransportDeleteTransformAction.java | 3 +- .../action/TransportPutTransformAction.java | 3 +- .../action/TransportResetTransformAction.java | 3 +- .../action/TransportStartTransformAction.java | 3 +- .../TransportUpgradeTransformsAction.java | 3 +- .../TransportGetWatcherSettingsAction.java | 3 +- .../TransportUpdateWatcherSettingsAction.java | 3 +- .../TransportWatcherServiceAction.java | 2 +- 203 files changed, 444 insertions(+), 234 deletions(-) diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/CreateDataStreamTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/CreateDataStreamTransportAction.java index 8b35370cbc473..7964934004dd0 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/CreateDataStreamTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/CreateDataStreamTransportAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.MetadataCreateDataStreamService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.SystemDataStreamDescriptor; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; @@ -48,7 +49,7 @@ public CreateDataStreamTransportAction( actionFilters, CreateDataStreamAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataCreateDataStreamService = metadataCreateDataStreamService; this.systemIndices = systemIndices; diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DeleteDataStreamTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DeleteDataStreamTransportAction.java index 904b918fe5ae4..e756ba32ec699 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DeleteDataStreamTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DeleteDataStreamTransportAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.Index; import org.elasticsearch.indices.SystemIndices; @@ -68,7 +69,7 @@ public DeleteDataStreamTransportAction( actionFilters, DeleteDataStreamAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java index 8eda42ed43fa2..73af952af524d 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexMode; @@ -67,7 +68,7 @@ public GetDataStreamsTransportAction( GetDataStreamAction.Request::new, indexNameExpressionResolver, GetDataStreamAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; clusterSettings = clusterService.getClusterSettings(); diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/MigrateToDataStreamTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/MigrateToDataStreamTransportAction.java index a38c83bc49490..ba013e46926d3 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/MigrateToDataStreamTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/MigrateToDataStreamTransportAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.MetadataMigrateToDataStreamService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -47,7 +48,7 @@ public MigrateToDataStreamTransportAction( actionFilters, MigrateToDataStreamAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataMigrateToDataStreamService = new MetadataMigrateToDataStreamService( threadPool, diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/ModifyDataStreamsTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/ModifyDataStreamsTransportAction.java index af19e1ebc91f9..97f081575d748 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/ModifyDataStreamsTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/ModifyDataStreamsTransportAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.MetadataDataStreamsService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public ModifyDataStreamsTransportAction( actionFilters, ModifyDataStreamsAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataDataStreamsService = metadataDataStreamsService; } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/PromoteDataStreamTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/PromoteDataStreamTransportAction.java index f009488b07e61..e048393494139 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/PromoteDataStreamTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/PromoteDataStreamTransportAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; @@ -50,7 +51,7 @@ public PromoteDataStreamTransportAction( actionFilters, PromoteDataStreamAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportDeleteDataStreamLifecycleAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportDeleteDataStreamLifecycleAction.java index 6a2591b2d730d..c9abf81cd2836 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportDeleteDataStreamLifecycleAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportDeleteDataStreamLifecycleAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.MetadataDataStreamsService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.datastreams.action.DataStreamsActionUtil; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; @@ -53,7 +54,7 @@ public TransportDeleteDataStreamLifecycleAction( actionFilters, DeleteDataStreamLifecycleAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataDataStreamsService = metadataDataStreamsService; this.systemIndices = systemIndices; diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportExplainDataStreamLifecycleAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportExplainDataStreamLifecycleAction.java index aa1313ca2ae6b..f9cd68acfa072 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportExplainDataStreamLifecycleAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportExplainDataStreamLifecycleAction.java @@ -61,7 +61,7 @@ public TransportExplainDataStreamLifecycleAction( ExplainDataStreamLifecycleAction.Request::new, indexNameExpressionResolver, ExplainDataStreamLifecycleAction.Response::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.errorStore = dataLifecycleServiceErrorStore; } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportGetDataStreamLifecycleAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportGetDataStreamLifecycleAction.java index 08ff707c29c11..e90f80cec9877 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportGetDataStreamLifecycleAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportGetDataStreamLifecycleAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.datastreams.action.DataStreamsActionUtil; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -55,7 +56,7 @@ public TransportGetDataStreamLifecycleAction( GetDataStreamLifecycleAction.Request::new, indexNameExpressionResolver, GetDataStreamLifecycleAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); clusterSettings = clusterService.getClusterSettings(); } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportPutDataStreamLifecycleAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportPutDataStreamLifecycleAction.java index a96d4de83e7c8..b893f2b461230 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportPutDataStreamLifecycleAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/action/TransportPutDataStreamLifecycleAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.MetadataDataStreamsService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.datastreams.action.DataStreamsActionUtil; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; @@ -52,7 +53,7 @@ public TransportPutDataStreamLifecycleAction( actionFilters, PutDataStreamLifecycleAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataDataStreamsService = metadataDataStreamsService; this.systemIndices = systemIndices; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java index 09ce5606b0a74..68302df47d6f2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java @@ -71,7 +71,7 @@ public TransportClusterAllocationExplainAction( ClusterAllocationExplainRequest::new, indexNameExpressionResolver, ClusterAllocationExplainResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.clusterInfoService = clusterInfoService; this.snapshotsInfoService = snapshotsInfoService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceAction.java index 12e315c6b6bf6..4360d7c1925f6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportDeleteDesiredBalanceAction.java @@ -56,7 +56,7 @@ public TransportDeleteDesiredBalanceAction( DesiredBalanceRequest::new, indexNameExpressionResolver, in -> ActionResponse.Empty.INSTANCE, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.resetDesiredBalanceTaskQueue = shardsAllocator instanceof DesiredBalanceShardsAllocator allocator diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java index 15ddc51e5c3bd..b585e891a5903 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java @@ -67,7 +67,7 @@ public TransportGetDesiredBalanceAction( DesiredBalanceRequest::new, indexNameExpressionResolver, DesiredBalanceResponse::from, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.desiredBalanceShardsAllocator = shardsAllocator instanceof DesiredBalanceShardsAllocator allocator ? allocator : null; this.clusterInfoService = clusterInfoService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java index f1f8b2b8d629c..57332429135b6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.tasks.Task; @@ -81,7 +82,7 @@ public TransportAddVotingConfigExclusionsAction( AddVotingConfigExclusionsRequest::new, indexNameExpressionResolver, in -> ActionResponse.Empty.INSTANCE, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); maxVotingConfigExclusions = MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(settings); diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java index f38e8caa676e2..46069f01ecda3 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.tasks.Task; @@ -63,7 +64,7 @@ public TransportClearVotingConfigExclusionsAction( ClearVotingConfigExclusionsRequest::new, indexNameExpressionResolver, in -> ActionResponse.Empty.INSTANCE, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.reconfigurator = reconfigurator; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDeleteDesiredNodesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDeleteDesiredNodesAction.java index 0efcf475497b9..48ea8beef2fd4 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDeleteDesiredNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDeleteDesiredNodesAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.service.MasterServiceTaskQueue; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -49,7 +50,7 @@ public TransportDeleteDesiredNodesAction( DeleteDesiredNodesAction.Request::new, indexNameExpressionResolver, in -> ActionResponse.Empty.INSTANCE, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.taskQueue = clusterService.createTaskQueue("delete-desired-nodes", Priority.HIGH, new DeleteDesiredNodesExecutor()); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportGetDesiredNodesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportGetDesiredNodesAction.java index a8c2bedee6bbf..e06918355e7a9 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportGetDesiredNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportGetDesiredNodesAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -43,7 +44,7 @@ public TransportGetDesiredNodesAction( GetDesiredNodesAction.Request::new, indexNameExpressionResolver, GetDesiredNodesAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesAction.java index 1667cbb3c92bf..0120718361877 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.service.MasterServiceTaskQueue; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -63,7 +64,7 @@ public TransportUpdateDesiredNodesAction( UpdateDesiredNodesRequest::new, indexNameExpressionResolver, UpdateDesiredNodesResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.settingsValidator = settingsValidator; this.taskQueue = clusterService.createTaskQueue( diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java index 309f43c966fee..935cc15eefe51 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java @@ -70,7 +70,8 @@ public TransportClusterHealthAction( ClusterHealthRequest::new, indexNameExpressionResolver, ClusterHealthResponse::new, - ThreadPool.Names.MANAGEMENT // fork to management since the health computation can become expensive for large cluster states + // fork to management since the health computation can become expensive for large cluster states. + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.allocationService = allocationService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java index 46a46f8d3f44c..652f5102769ae 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -77,7 +78,7 @@ public TransportGetFeatureUpgradeStatusAction( GetFeatureUpgradeStatusRequest::new, indexNameExpressionResolver, GetFeatureUpgradeStatusResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); assert Version.CURRENT.major == 8 : "Once we begin working on 9.x, we need to update our migration classes"; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java index 5e94e582035c4..bd5114bf91ed2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.tasks.Task; @@ -63,7 +64,7 @@ public TransportPostFeatureUpgradeAction( PostFeatureUpgradeRequest::new, indexNameExpressionResolver, PostFeatureUpgradeResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; this.persistentTasksService = persistentTasksService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/shutdown/TransportPrevalidateNodeRemovalAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/shutdown/TransportPrevalidateNodeRemovalAction.java index f3ce39a0cdbf1..fce38ab63c302 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/shutdown/TransportPrevalidateNodeRemovalAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/shutdown/TransportPrevalidateNodeRemovalAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Strings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.shard.ShardId; @@ -73,7 +74,7 @@ public TransportPrevalidateNodeRemovalAction( PrevalidateNodeRemovalRequest::new, indexNameExpressionResolver, PrevalidateNodeRemovalResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java index a25b73bfe3e55..bd9382aeaa758 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.SuppressForbidden; @@ -80,7 +81,7 @@ public TransportCleanupRepositoryAction( CleanupRepositoryRequest::new, indexNameExpressionResolver, CleanupRepositoryResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.repositoriesService = repositoriesService; // We add a state applier that will remove any dangling repository cleanup actions on master failover. diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java index c2ff146f26869..b1f78408c7829 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -51,7 +52,7 @@ public TransportDeleteRepositoryAction( actionFilters, DeleteRepositoryRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.repositoriesService = repositoriesService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java index ff54b4f4bb4da..b31dde0f75613 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.repositories.RepositoryMissingException; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -55,7 +56,7 @@ public TransportGetRepositoriesAction( GetRepositoriesRequest::new, indexNameExpressionResolver, GetRepositoriesResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java index 56a304591b04a..bb17b0d8ab8fe 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -51,7 +52,7 @@ public TransportPutRepositoryAction( actionFilters, PutRepositoryRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.repositoriesService = repositoriesService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java index 4335dea3861e3..353cc2994afc7 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -48,7 +49,7 @@ public TransportVerifyRepositoryAction( VerifyRepositoryRequest::new, indexNameExpressionResolver, VerifyRepositoryResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.repositoriesService = repositoriesService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java index 055af211e19ff..4c7c4ec8f15a2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java @@ -35,6 +35,7 @@ import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; @@ -72,7 +73,7 @@ public TransportClusterRerouteAction( ClusterRerouteRequest::new, indexNameExpressionResolver, ClusterRerouteResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.allocationService = allocationService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterGetSettingsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterGetSettingsAction.java index 2ef9ad7def5ee..302efb5867065 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterGetSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterGetSettingsAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -47,7 +48,7 @@ public TransportClusterGetSettingsAction( ClusterGetSettingsAction.Request::new, indexNameExpressionResolver, ClusterGetSettingsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.settingsFilter = settingsFilter; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java index 3963faff454e9..da44265f87436 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsUpdater; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.reservedstate.action.ReservedClusterSettingsAction; import org.elasticsearch.tasks.Task; @@ -68,7 +69,7 @@ public TransportClusterUpdateSettingsAction( ClusterUpdateSettingsRequest::new, indexNameExpressionResolver, ClusterUpdateSettingsResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.clusterSettings = clusterSettings; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java index 9cbcbf821c75a..ccfd192246c0a 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java @@ -57,7 +57,7 @@ public TransportClusterSearchShardsAction( ClusterSearchShardsRequest::new, indexNameExpressionResolver, ClusterSearchShardsResponse::new, - ThreadPool.Names.SEARCH_COORDINATION + threadPool.executor(ThreadPool.Names.SEARCH_COORDINATION) ); this.indicesService = indicesService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java index 1cb8af81d12c3..1a37cd0204c30 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -47,7 +48,7 @@ public TransportCloneSnapshotAction( actionFilters, CloneSnapshotRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.snapshotsService = snapshotsService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java index 66cf927af7d61..8d776b7ae6ecb 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -46,7 +47,7 @@ public TransportCreateSnapshotAction( CreateSnapshotRequest::new, indexNameExpressionResolver, CreateSnapshotResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.snapshotsService = snapshotsService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java index 32fdb285cb808..df7a5e5595055 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -46,7 +47,7 @@ public TransportDeleteSnapshotAction( actionFilters, DeleteSnapshotRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.snapshotsService = snapshotsService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportResetFeatureStateAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportResetFeatureStateAction.java index c0c928e2743d5..4b7b91099ff12 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportResetFeatureStateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportResetFeatureStateAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -54,7 +55,7 @@ public TransportResetFeatureStateAction( ResetFeatureStateRequest::fromStream, indexNameExpressionResolver, ResetFeatureStateResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; this.client = client; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportSnapshottableFeaturesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportSnapshottableFeaturesAction.java index 9e512fa604888..e7deabd341312 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportSnapshottableFeaturesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/features/TransportSnapshottableFeaturesAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -46,7 +47,7 @@ public TransportSnapshottableFeaturesAction( GetSnapshottableFeaturesRequest::new, indexNameExpressionResolver, GetSnapshottableFeaturesResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java index 1d8966c6b3815..ca910a8d94078 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java @@ -85,11 +85,12 @@ public TransportGetSnapshotsAction( GetSnapshotsRequest::new, indexNameExpressionResolver, GetSnapshotsResponse::new, - ThreadPool.Names.MANAGEMENT // Execute this on the management pool because creating the response can become fairly expensive - // for large repositories in the verbose=false case when there are a lot of indices per snapshot. - // This is intentionally not using the snapshot_meta pool because that pool is sized rather large - // to accommodate concurrent IO and could consume excessive CPU resources through concurrent - // verbose=false requests that are CPU bound only. + // Execute this on the management pool because creating the response can become fairly expensive + // for large repositories in the verbose=false case when there are a lot of indices per snapshot. + // This is intentionally not using the snapshot_meta pool because that pool is sized rather large + // to accommodate concurrent IO and could consume excessive CPU resources through concurrent + // verbose=false requests that are CPU bound only. + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.repositoriesService = repositoriesService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/TransportGetShardSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/TransportGetShardSnapshotAction.java index bc9bfcfcf1c1d..7b1c5f9a3e290 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/TransportGetShardSnapshotAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/TransportGetShardSnapshotAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.repositories.IndexSnapshotsService; import org.elasticsearch.repositories.RepositoriesService; @@ -57,7 +58,7 @@ public TransportGetShardSnapshotAction( GetShardSnapshotRequest::new, indexNameExpressionResolver, GetShardSnapshotResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexSnapshotsService = new IndexSnapshotsService(repositoriesService); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java index 33422ca16cf28..b7190de319d97 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.snapshots.RestoreService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -46,7 +47,7 @@ public TransportRestoreSnapshotAction( RestoreSnapshotRequest::new, indexNameExpressionResolver, RestoreSnapshotResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.restoreService = restoreService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java index eb6e7202de5c8..2a6f0325be1d2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java @@ -91,7 +91,8 @@ public TransportSnapshotsStatusAction( SnapshotsStatusRequest::new, indexNameExpressionResolver, SnapshotsStatusResponse::new, - ThreadPool.Names.SNAPSHOT_META // building the response is somewhat expensive for large snapshots so we fork + // building the response is somewhat expensive for large snapshots, so we fork. + threadPool.executor(ThreadPool.Names.SNAPSHOT_META) ); this.repositoriesService = repositoriesService; this.client = client; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java index c2684c4becf3c..5af2c12d39bc1 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -62,7 +62,7 @@ public TransportClusterStateAction( ClusterStateRequest::new, indexNameExpressionResolver, ClusterStateResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java index 3a044273f7aca..dfb3745d4101a 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -41,7 +42,7 @@ public TransportDeleteStoredScriptAction( actionFilters, DeleteStoredScriptRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java index 8b1353dbc7f23..f5c674df2e475 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -41,7 +42,7 @@ public TransportGetStoredScriptAction( GetStoredScriptRequest::new, indexNameExpressionResolver, GetStoredScriptResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java index 28c4f61a5eb9b..8025d983d2668 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -44,7 +45,7 @@ public TransportPutStoredScriptAction( actionFilters, PutStoredScriptRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.scriptService = scriptService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java index 2470bc103a53e..ba7e799095ef8 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.PendingClusterTask; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -50,7 +51,7 @@ public TransportPendingClusterTasksAction( PendingClusterTasksRequest::new, indexNameExpressionResolver, PendingClusterTasksResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.clusterService = clusterService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java index 70d457361f759..fd3a200075d75 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException; @@ -81,7 +82,7 @@ public TransportIndicesAliasesAction( actionFilters, IndicesAliasesRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexAliasesService = indexAliasesService; this.requestValidators = Objects.requireNonNull(requestValidators); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java index 3ebcdc8129bc3..456b2cc7b899f 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java @@ -61,7 +61,7 @@ public TransportGetAliasesAction( GetAliasesRequest::new, indexNameExpressionResolver, GetAliasesResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.systemIndices = systemIndices; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java index 1f819c00d9cac..0103e8abf654a 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -71,7 +72,7 @@ public TransportCloseIndexAction( CloseIndexRequest::new, indexNameExpressionResolver, CloseIndexResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexStateService = indexStateService; this.destructiveOperations = destructiveOperations; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/create/AutoCreateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/create/AutoCreateAction.java index c62b689d58e78..1cec71d2abe53 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/create/AutoCreateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/create/AutoCreateAction.java @@ -40,6 +40,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.indices.SystemDataStreamDescriptor; @@ -101,7 +102,7 @@ public TransportAction( CreateIndexRequest::new, indexNameExpressionResolver, CreateIndexResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.systemIndices = systemIndices; this.createIndexService = createIndexService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java index 3deb70df92d88..c03cba9b40a33 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.indices.SystemIndices.SystemIndexAccessLevel; @@ -66,7 +67,7 @@ public TransportCreateIndexAction( CreateIndexRequest::new, indexNameExpressionResolver, CreateIndexResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.createIndexService = createIndexService; this.systemIndices = systemIndices; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java index 53c9d58046fb7..1207c2c1e60ff 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java @@ -71,7 +71,7 @@ public TransportDeleteDanglingIndexAction( actionFilters, DeleteDanglingIndexRequest::new, indexNameExpressionResolver, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.settings = settings; this.nodeClient = nodeClient; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java index e7a50fc10e65e..8fe6e0b67e827 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.MetadataDeleteIndexService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -58,7 +59,7 @@ public TransportDeleteIndexAction( actionFilters, DeleteIndexRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.deleteIndexService = deleteIndexService; this.destructiveOperations = destructiveOperations; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportFlushAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportFlushAction.java index 6fce79e31b911..ade775db9c755 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportFlushAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportFlushAction.java @@ -1,3 +1,4 @@ + /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -48,7 +49,7 @@ public TransportFlushAction( actionFilters, indexNameExpressionResolver, TransportShardFlushAction.TYPE, - ThreadPool.Names.FLUSH + transportService.getThreadPool().executor(ThreadPool.Names.FLUSH) ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java index 34ca71b512979..35a76ada36678 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.MetadataMappingService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.tasks.Task; @@ -53,7 +54,7 @@ public TransportAutoPutMappingAction( actionFilters, PutMappingRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataMappingService = metadataMappingService; this.systemIndices = systemIndices; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportPutMappingAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportPutMappingAction.java index 055c49be43b96..5cdef40a393b6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportPutMappingAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/put/TransportPutMappingAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.indices.SystemIndexDescriptor; @@ -68,7 +69,7 @@ public TransportPutMappingAction( actionFilters, PutMappingRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.metadataMappingService = metadataMappingService; this.requestValidators = Objects.requireNonNull(requestValidators); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java index df2c76d95acbb..309e9d841c97a 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/open/TransportOpenIndexAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexStateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -58,7 +59,7 @@ public TransportOpenIndexAction( OpenIndexRequest::new, indexNameExpressionResolver, OpenIndexResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexStateService = indexStateService; this.destructiveOperations = destructiveOperations; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java index d30ef55d526e4..731257ddabbad 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexStateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -63,7 +64,7 @@ public TransportAddIndexBlockAction( AddIndexBlockRequest::new, indexNameExpressionResolver, AddIndexBlockResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexStateService = indexStateService; this.destructiveOperations = destructiveOperations; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportRefreshAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportRefreshAction.java index ceb940502da5d..7537e74e2c780 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportRefreshAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportRefreshAction.java @@ -50,7 +50,7 @@ public TransportRefreshAction( actionFilters, indexNameExpressionResolver, TransportShardRefreshAction.TYPE, - ThreadPool.Names.REFRESH + transportService.getThreadPool().executor(ThreadPool.Names.REFRESH) ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java index 5519eb9707f23..fce8402114ff6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java @@ -40,6 +40,7 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.tasks.CancellableTask; @@ -87,7 +88,7 @@ public TransportRolloverAction( RolloverRequest::new, indexNameExpressionResolver, RolloverResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.rolloverTaskQueue = clusterService.createTaskQueue( diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java index 8146528050ecb..e11c5a1a6103d 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java @@ -58,7 +58,7 @@ public TransportGetSettingsAction( GetSettingsRequest::new, indexNameExpressionResolver, GetSettingsResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.settingsFilter = settingsFilter; this.indexScopedSettings = indexedScopedSettings; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java index 56facc0c0f1f3..b613eab0d731c 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndices; @@ -66,7 +67,7 @@ public TransportUpdateSettingsAction( actionFilters, UpdateSettingsRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.updateSettingsService = updateSettingsService; this.systemIndices = systemIndices; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java index 8d84b6cc480be..4f04414cff1ac 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThrottledIterator; import org.elasticsearch.core.Releasable; import org.elasticsearch.gateway.TransportNodesListGatewayStartedShards; @@ -83,7 +84,7 @@ public TransportIndicesShardStoresAction( IndicesShardStoresRequest::new, indexNameExpressionResolver, IndicesShardStoresResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java index 3aa54cec8530c..30197d102dc47 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.tasks.Task; @@ -89,7 +90,7 @@ protected TransportResizeAction( ResizeRequest::new, indexNameExpressionResolver, ResizeResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.createIndexService = createIndexService; this.client = client; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java index 8926b344ea7dd..e1987e822e4f4 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -50,7 +51,7 @@ public TransportDeleteComponentTemplateAction( actionFilters, DeleteComponentTemplateAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java index f027455c3a0f2..2f3b95ec0c714 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -51,7 +52,7 @@ public TransportDeleteComposableIndexTemplateAction( actionFilters, DeleteComposableIndexTemplateAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java index 536cfeb75d23e..56c341fe649cc 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -50,7 +51,7 @@ public TransportDeleteIndexTemplateAction( actionFilters, DeleteIndexTemplateRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java index a29854f079baf..e76dc0f46eea2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -52,7 +53,7 @@ public TransportGetComponentTemplateAction( GetComponentTemplateAction.Request::new, indexNameExpressionResolver, GetComponentTemplateAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); clusterSettings = clusterService.getClusterSettings(); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java index f07f697d915fd..c9b2a23c38828 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -52,7 +53,7 @@ public TransportGetComposableIndexTemplateAction( GetComposableIndexTemplateAction.Request::new, indexNameExpressionResolver, GetComposableIndexTemplateAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); clusterSettings = clusterService.getClusterSettings(); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java index 55036ab63df3c..ae5bbe38de801 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -45,7 +46,7 @@ public TransportGetIndexTemplatesAction( GetIndexTemplatesRequest::new, indexNameExpressionResolver, GetIndexTemplatesResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java index d69f7dc8a8607..42c71bb39e633 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.IndexSettingProvider; import org.elasticsearch.index.IndexSettingProviders; @@ -91,7 +92,7 @@ public TransportSimulateIndexTemplateAction( SimulateIndexTemplateRequest::new, indexNameExpressionResolver, SimulateIndexTemplateResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; this.xContentRegistry = xContentRegistry; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java index d82f0b66be502..b99f436dd86f9 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexSettingProvider; import org.elasticsearch.index.IndexSettingProviders; import org.elasticsearch.indices.IndicesService; @@ -78,7 +79,7 @@ public TransportSimulateTemplateAction( SimulateTemplateAction.Request::new, indexNameExpressionResolver, SimulateIndexTemplateResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; this.xContentRegistry = xContentRegistry; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java index f8b7b80ce696c..4e1776a49d21c 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -55,7 +56,7 @@ public TransportPutComponentTemplateAction( actionFilters, PutComponentTemplateAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; this.indexScopedSettings = indexScopedSettings; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java index 3520d921c2249..541aa43c72490 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.ReservedStateMetadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -56,7 +57,7 @@ public TransportPutComposableIndexTemplateAction( actionFilters, PutComposableIndexTemplateAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java index 994eb0220a22c..73f3f10680784 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -58,7 +59,7 @@ public TransportPutIndexTemplateAction( actionFilters, PutIndexTemplateRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexTemplateService = indexTemplateService; this.indexScopedSettings = indexScopedSettings; diff --git a/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineTransportAction.java b/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineTransportAction.java index acb2e91433623..c8a8e175c69a9 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineTransportAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -45,7 +46,7 @@ public DeletePipelineTransportAction( actionFilters, DeletePipelineRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.ingestService = ingestService; } diff --git a/server/src/main/java/org/elasticsearch/action/ingest/GetPipelineTransportAction.java b/server/src/main/java/org/elasticsearch/action/ingest/GetPipelineTransportAction.java index 44e72ce17b178..8ba251e911431 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/GetPipelineTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/GetPipelineTransportAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -41,7 +42,7 @@ public GetPipelineTransportAction( GetPipelineRequest::new, indexNameExpressionResolver, GetPipelineResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java index 80359b8dd2a3c..65d5ad5807ecb 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -51,7 +52,7 @@ public PutPipelineTransportAction( actionFilters, PutPipelineRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); // This client is only used to perform an internal implementation detail, // so uses an internal origin context rather than the user context diff --git a/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedTransportMasterNodeAction.java b/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedTransportMasterNodeAction.java index 0e260ab247261..ce76a64009030 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedTransportMasterNodeAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedTransportMasterNodeAction.java @@ -15,6 +15,8 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.concurrent.Executor; + /** * Base class for the common case of a {@link TransportMasterNodeAction} that responds with an {@link AcknowledgedResponse}. */ @@ -30,7 +32,7 @@ protected AcknowledgedTransportMasterNodeAction( ActionFilters actionFilters, Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver, - String executor + Executor executor ) { super( actionName, @@ -54,7 +56,7 @@ protected AcknowledgedTransportMasterNodeAction( ActionFilters actionFilters, Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver, - String executor + Executor executor ) { super( actionName, diff --git a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java index 8f1bbffd09c0f..c14d8adb04e46 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java @@ -67,6 +67,9 @@ public abstract class TransportMasterNodeAction response, String executor + ) { + this( + actionName, + transportService, + clusterService, + threadPool, + actionFilters, + request, + indexNameExpressionResolver, + response, + threadPool.executor(executor) + ); + } + + protected TransportMasterNodeAction( + String actionName, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver, + Writeable.Reader response, + Executor executor ) { this( actionName, @@ -102,14 +129,14 @@ protected TransportMasterNodeAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader response, - String executor + Executor executor ) { super(actionName, canTripCircuitBreaker, transportService, actionFilters, request); this.transportService = transportService; this.clusterService = clusterService; this.threadPool = threadPool; this.indexNameExpressionResolver = indexNameExpressionResolver; - this.executor = threadPool.executor(executor); + this.executor = executor; this.responseReader = response; } diff --git a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeReadAction.java b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeReadAction.java index ea0c4a109b600..89de477ed0785 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeReadAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeReadAction.java @@ -16,6 +16,8 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.concurrent.Executor; + /** * A base class for read operations that needs to be performed on the master node. * Can also be executed on the local node if needed. @@ -32,7 +34,7 @@ protected TransportMasterNodeReadAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader response, - String executor + Executor executor ) { this( actionName, @@ -58,7 +60,7 @@ protected TransportMasterNodeReadAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver, Writeable.Reader response, - String executor + Executor executor ) { super( actionName, diff --git a/server/src/main/java/org/elasticsearch/action/support/master/info/TransportClusterInfoAction.java b/server/src/main/java/org/elasticsearch/action/support/master/info/TransportClusterInfoAction.java index da8b93a855dc3..b0ab3cddb9729 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/info/TransportClusterInfoAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/info/TransportClusterInfoAction.java @@ -43,7 +43,7 @@ public TransportClusterInfoAction( request, indexNameExpressionResolver, response, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportBroadcastReplicationAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportBroadcastReplicationAction.java index 94c90a0efcd83..6eafd692e2b4e 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportBroadcastReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportBroadcastReplicationAction.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; /** * Base class for requests that should be executed on all shards of an index or several indices. @@ -51,7 +52,7 @@ public abstract class TransportBroadcastReplicationAction< private final ClusterService clusterService; private final IndexNameExpressionResolver indexNameExpressionResolver; private final NodeClient client; - private final String executor; + private final Executor executor; public TransportBroadcastReplicationAction( String name, @@ -62,7 +63,7 @@ public TransportBroadcastReplicationAction( ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, ActionType replicatedBroadcastShardAction, - String executor + Executor executor ) { super(name, transportService, actionFilters, requestReader); this.client = client; @@ -74,7 +75,7 @@ public TransportBroadcastReplicationAction( @Override protected void doExecute(Task task, Request request, ActionListener listener) { - clusterService.threadPool().executor(executor).execute(ActionRunnable.wrap(listener, createAsyncAction(task, request))); + executor.execute(ActionRunnable.wrap(listener, createAsyncAction(task, request))); } private CheckedConsumer, Exception> createAsyncAction(Task task, Request request) { diff --git a/server/src/main/java/org/elasticsearch/health/node/FetchHealthInfoCacheAction.java b/server/src/main/java/org/elasticsearch/health/node/FetchHealthInfoCacheAction.java index 32ebbc494589b..6433e3028dbfa 100644 --- a/server/src/main/java/org/elasticsearch/health/node/FetchHealthInfoCacheAction.java +++ b/server/src/main/java/org/elasticsearch/health/node/FetchHealthInfoCacheAction.java @@ -113,7 +113,7 @@ public TransportAction( actionFilters, FetchHealthInfoCacheAction.Request::new, FetchHealthInfoCacheAction.Response::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.nodeHealthOverview = nodeHealthOverview; } diff --git a/server/src/main/java/org/elasticsearch/health/node/UpdateHealthInfoCacheAction.java b/server/src/main/java/org/elasticsearch/health/node/UpdateHealthInfoCacheAction.java index 265c084dabd2b..1499c278a4209 100644 --- a/server/src/main/java/org/elasticsearch/health/node/UpdateHealthInfoCacheAction.java +++ b/server/src/main/java/org/elasticsearch/health/node/UpdateHealthInfoCacheAction.java @@ -114,7 +114,7 @@ public TransportAction( actionFilters, UpdateHealthInfoCacheAction.Request::new, AcknowledgedResponse::readFrom, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.nodeHealthOverview = nodeHealthOverview; } diff --git a/server/src/main/java/org/elasticsearch/health/node/action/TransportHealthNodeAction.java b/server/src/main/java/org/elasticsearch/health/node/action/TransportHealthNodeAction.java index a61bbeb76a0c1..19cc2458a9487 100644 --- a/server/src/main/java/org/elasticsearch/health/node/action/TransportHealthNodeAction.java +++ b/server/src/main/java/org/elasticsearch/health/node/action/TransportHealthNodeAction.java @@ -31,6 +31,8 @@ import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; +import java.util.concurrent.Executor; + import static org.elasticsearch.core.Strings.format; /** @@ -56,7 +58,7 @@ public abstract class TransportHealthNodeAction responseReader; @@ -69,7 +71,7 @@ protected TransportHealthNodeAction( ActionFilters actionFilters, Writeable.Reader request, Writeable.Reader response, - String executor + Executor executor ) { super(actionName, true, transportService, actionFilters, request); this.transportService = transportService; @@ -103,7 +105,7 @@ protected void doExecute(Task task, final Request request, ActionListener { + executor.execute(() -> { try { if (isTaskCancelled(task)) { listener.onFailure(new TaskCancelledException("Task was cancelled")); diff --git a/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java index c9f9f1ff5e6e6..cb7652bdc7b03 100644 --- a/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java +++ b/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java @@ -145,7 +145,7 @@ public TransportAction( Request::new, indexNameExpressionResolver, PersistentTaskResponse::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.persistentTasksClusterService = persistentTasksClusterService; } diff --git a/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java index 6fc3bcc2a5dfa..7fac04a63993e 100644 --- a/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java +++ b/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java @@ -121,7 +121,7 @@ public TransportAction( Request::new, indexNameExpressionResolver, PersistentTaskResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.persistentTasksClusterService = persistentTasksClusterService; } diff --git a/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java index 9cc1b2add4780..c719eb318d571 100644 --- a/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java +++ b/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java @@ -188,7 +188,7 @@ public TransportAction( Request::new, indexNameExpressionResolver, PersistentTaskResponse::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.persistentTasksClusterService = persistentTasksClusterService; NodePersistentTasksExecutor executor = new NodePersistentTasksExecutor(threadPool); diff --git a/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java b/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java index 6f14f543fc61b..6074cc0e4ea35 100644 --- a/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java +++ b/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java @@ -152,7 +152,7 @@ public TransportAction( Request::new, indexNameExpressionResolver, PersistentTaskResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.persistentTasksClusterService = persistentTasksClusterService; } diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 7e75beae5bb81..a4ce775a1c2bd 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -67,6 +67,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.SuppressForbidden; @@ -3392,7 +3393,7 @@ private class UpdateSnapshotStatusAction extends TransportMasterNodeAction< UpdateIndexShardSnapshotStatusRequest::new, indexNameExpressionResolver, in -> ActionResponse.Empty.INSTANCE, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java index bb1cf85013498..e10fd960a7554 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java @@ -37,7 +37,6 @@ import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.cache.query.QueryCacheStats; @@ -79,7 +78,6 @@ import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -302,7 +300,6 @@ public void testEvaluateWithoutMetadata() { } public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPrimariesFromWriteIndex() throws Exception { - final TransportService mockTransportService = mock(TransportService.class); final ClusterService mockClusterService = mock(ClusterService.class); final DiscoveryNode mockNode = mock(DiscoveryNode.class); when(mockNode.getId()).thenReturn("mocknode"); @@ -357,11 +354,8 @@ public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPr WriteLoadForecaster.DEFAULT ); - // TODO: temporary, remove in #97879 - when(mockTransportService.getThreadPool()).thenReturn(mockThreadPool); - when(mockThreadPool.executor(anyString())).thenReturn(EsExecutors.DIRECT_EXECUTOR_SERVICE); final TransportRolloverAction transportRolloverAction = new TransportRolloverAction( - mockTransportService, + mock(TransportService.class), mockClusterService, mockThreadPool, mockActionFilters, diff --git a/server/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java b/server/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java index 1f8aedc51826e..dde9f3d3bd61f 100644 --- a/server/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/master/TransportMasterNodeActionTests.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor; import org.elasticsearch.core.AbstractRefCounted; import org.elasticsearch.core.RefCounted; @@ -77,6 +78,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; @@ -231,7 +233,7 @@ public void writeTo(StreamOutput out) throws IOException { class Action extends TransportMasterNodeAction { Action(String actionName, TransportService transportService, ClusterService clusterService, ThreadPool threadPool) { - this(actionName, transportService, clusterService, threadPool, ThreadPool.Names.SAME); + this(actionName, transportService, clusterService, threadPool, EsExecutors.DIRECT_EXECUTOR_SERVICE); } Action( @@ -239,7 +241,7 @@ class Action extends TransportMasterNodeAction { TransportService transportService, ClusterService clusterService, ThreadPool threadPool, - String executor + Executor executor ) { super( actionName, @@ -267,7 +269,7 @@ protected ClusterBlockException checkBlock(Request request, ClusterState state) class ReservedStateAction extends Action { ReservedStateAction(String actionName, TransportService transportService, ClusterService clusterService, ThreadPool threadPool) { - super(actionName, transportService, clusterService, threadPool, ThreadPool.Names.SAME); + super(actionName, transportService, clusterService, threadPool, EsExecutors.DIRECT_EXECUTOR_SERVICE); } @Override @@ -282,7 +284,7 @@ class FakeClusterStateUpdateAction extends TransportMasterNodeAction listener = new PlainActionFuture<>(); ActionTestUtils.execute( - new Action("internal:testAction", transportService, clusterService, threadPool, executorName), + new Action("internal:testAction", transportService, clusterService, threadPool, threadPool.executor(executorName)), task, request, listener @@ -714,7 +716,13 @@ public void testGlobalBlocksAreCheckedAfterIndexNotFoundException() throws Excep ).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); setState(clusterService, stateWithBlockWithoutIndexMetadata); - Action action = new Action("internal:testAction", transportService, clusterService, threadPool, ThreadPool.Names.SAME) { + Action action = new Action( + "internal:testAction", + transportService, + clusterService, + threadPool, + EsExecutors.DIRECT_EXECUTOR_SERVICE + ) { final IndexNameExpressionResolver indexNameExpressionResolver = TestIndexNameExpressionResolver.newInstance(); @Override @@ -755,7 +763,13 @@ public void testGlobalBlocksAreCheckedAfterIndexNotFoundExceptionTimesOutIfIndex ).blocks(ClusterBlocks.builder().addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)).build(); setState(clusterService, stateWithBlockWithoutIndexMetadata); - Action action = new Action("internal:testAction", transportService, clusterService, threadPool, ThreadPool.Names.SAME) { + Action action = new Action( + "internal:testAction", + transportService, + clusterService, + threadPool, + EsExecutors.DIRECT_EXECUTOR_SERVICE + ) { final IndexNameExpressionResolver indexNameExpressionResolver = TestIndexNameExpressionResolver.newInstance(); @Override @@ -786,7 +800,13 @@ public void testRejectImmutableConflictClusterStateUpdate() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).metadata(metadata).build(); - Action noHandler = new Action("internal:testAction", transportService, clusterService, threadPool, ThreadPool.Names.SAME); + Action noHandler = new Action( + "internal:testAction", + transportService, + clusterService, + threadPool, + EsExecutors.DIRECT_EXECUTOR_SERVICE + ); assertFalse(noHandler.supportsReservedState()); @@ -806,7 +826,7 @@ public void testRejectImmutableConflictClusterStateUpdate() { transportService, clusterService, threadPool, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); assertTrue(action.supportsReservedState()); diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/BroadcastReplicationTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/BroadcastReplicationTests.java index 5202d3e1f080f..f0be7453d7b59 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/BroadcastReplicationTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/BroadcastReplicationTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.shard.ShardId; @@ -255,7 +256,7 @@ private class TestBroadcastReplicationAction extends TransportBroadcastReplicati actionFilters, indexNameExpressionResolver, null, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/server/src/test/java/org/elasticsearch/health/node/action/TransportHealthNodeActionTests.java b/server/src/test/java/org/elasticsearch/health/node/action/TransportHealthNodeActionTests.java index f7a74b47d8e4e..0781cf6614dac 100644 --- a/server/src/test/java/org/elasticsearch/health/node/action/TransportHealthNodeActionTests.java +++ b/server/src/test/java/org/elasticsearch/health/node/action/TransportHealthNodeActionTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskCancelledException; @@ -44,6 +45,7 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; @@ -156,7 +158,7 @@ public void writeTo(StreamOutput out) throws IOException { class Action extends TransportHealthNodeAction { Action(String actionName, TransportService transportService, ClusterService clusterService, ThreadPool threadPool) { - this(actionName, transportService, clusterService, threadPool, ThreadPool.Names.SAME); + this(actionName, transportService, clusterService, threadPool, EsExecutors.DIRECT_EXECUTOR_SERVICE); } Action( @@ -164,7 +166,7 @@ class Action extends TransportHealthNodeAction { TransportService transportService, ClusterService clusterService, ThreadPool threadPool, - String executor + Executor executor ) { super( actionName, @@ -194,7 +196,7 @@ class WaitForSignalAction extends Action { ThreadPool threadPool, CountDownLatch countDownLatch ) { - super(actionName, transportService, clusterService, threadPool, ThreadPool.Names.SAME); + super(actionName, transportService, clusterService, threadPool, EsExecutors.DIRECT_EXECUTOR_SERVICE); this.countDownLatch = countDownLatch; } diff --git a/server/src/test/java/org/elasticsearch/indices/settings/InternalOrPrivateSettingsPlugin.java b/server/src/test/java/org/elasticsearch/indices/settings/InternalOrPrivateSettingsPlugin.java index 7d0b6fe8b1cd4..02ee4b080834f 100644 --- a/server/src/test/java/org/elasticsearch/indices/settings/InternalOrPrivateSettingsPlugin.java +++ b/server/src/test/java/org/elasticsearch/indices/settings/InternalOrPrivateSettingsPlugin.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.tasks.Task; @@ -137,7 +138,7 @@ public TransportUpdateInternalOrPrivateAction( UpdateInternalOrPrivateAction.Request::new, indexNameExpressionResolver, UpdateInternalOrPrivateAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyAction.java index 280ce73b66e3e..1084efe09c3af 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyAction.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportDeleteAutoscalingPolicyAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -56,7 +57,7 @@ public TransportDeleteAutoscalingPolicyAction( actionFilters, DeleteAutoscalingPolicyAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingCapacityAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingCapacityAction.java index 5dff4c86434b2..a2024dec3b8ca 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingCapacityAction.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingCapacityAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.snapshots.SnapshotsInfoService; import org.elasticsearch.tasks.CancellableTask; @@ -68,7 +69,7 @@ public TransportGetAutoscalingCapacityAction( GetAutoscalingCapacityAction.Request::new, indexNameExpressionResolver, GetAutoscalingCapacityAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.snapshotsInfoService = snapshotsInfoService; this.nodeInfoService = nodeInfoService; diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingPolicyAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingPolicyAction.java index 21be3f94750fb..9157643578c7f 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingPolicyAction.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportGetAutoscalingPolicyAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -51,7 +52,7 @@ public TransportGetAutoscalingPolicyAction( GetAutoscalingPolicyAction.Request::new, indexNameExpressionResolver, GetAutoscalingPolicyAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker); } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java index ae6fc04778a80..71c3383add0d4 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/action/TransportPutAutoscalingPolicyAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.tasks.Task; @@ -84,7 +85,7 @@ public TransportPutAutoscalingPolicyAction( actionFilters, PutAutoscalingPolicyAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.policyValidator = policyValidator; this.autoscalingLicenseChecker = Objects.requireNonNull(autoscalingLicenseChecker); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java index 1a5a97fbbfda2..a4665ae56f08d 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -48,7 +49,7 @@ public TransportActivateAutoFollowPatternAction( actionFilters, Request::new, resolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportCcrStatsAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportCcrStatsAction.java index 93b23bf54d497..6b324ae901370 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportCcrStatsAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportCcrStatsAction.java @@ -55,7 +55,7 @@ public TransportCcrStatsAction( CcrStatsAction.Request::new, indexNameExpressionResolver, CcrStatsAction.Response::new, - Ccr.CCR_THREAD_POOL_NAME + threadPool.executor(Ccr.CCR_THREAD_POOL_NAME) ); this.client = client; this.ccrLicenseChecker = Objects.requireNonNull(ccrLicenseChecker); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternAction.java index 3544f2ee49341..0f41b6ba05dc9 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -45,7 +46,7 @@ public TransportDeleteAutoFollowPatternAction( actionFilters, DeleteAutoFollowPatternAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowInfoAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowInfoAction.java index f261417c40596..46c44c9b2392b 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowInfoAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportFollowInfoAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -53,7 +54,7 @@ public TransportFollowInfoAction( FollowInfoAction.Request::new, indexNameExpressionResolver, FollowInfoAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternAction.java index 69f5029d8d9d9..d596eae43abde 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -49,7 +50,7 @@ public TransportGetAutoFollowPatternAction( GetAutoFollowPatternAction.Request::new, indexNameExpressionResolver, GetAutoFollowPatternAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPauseFollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPauseFollowAction.java index 55d61174d3ac2..6989abdf1de01 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPauseFollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPauseFollowAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksService; @@ -51,7 +52,7 @@ public TransportPauseFollowAction( actionFilters, PauseFollowAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.persistentTasksService = persistentTasksService; } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java index a8b8332d5d7c5..88983d1e67d51 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.tasks.Task; @@ -70,7 +71,7 @@ public TransportPutAutoFollowPatternAction( actionFilters, PutAutoFollowPatternAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.remoteClientResponseExecutor = threadPool.executor(Ccr.CCR_THREAD_POOL_NAME); diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutFollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutFollowAction.java index 713da462a90be..b44f43f1ce925 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutFollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutFollowAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.license.LicenseUtils; @@ -87,7 +88,7 @@ public TransportPutFollowAction( PutFollowAction.Request::new, indexNameExpressionResolver, PutFollowAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexScopedSettings = indexScopedSettings; this.client = client; diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java index 2a2ac669bcc41..2e8ee39111ab7 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexSettings; @@ -105,7 +106,7 @@ public TransportResumeFollowAction( actionFilters, ResumeFollowAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.threadPool = threadPool; diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportUnfollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportUnfollowAction.java index 70ed2c7b066c7..481f5f1817be5 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportUnfollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportUnfollowAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.Index; @@ -76,7 +77,7 @@ public TransportUnfollowAction( actionFilters, UnfollowAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = Objects.requireNonNull(client); this.remoteClientResponseExecutor = threadPool.executor(Ccr.CCR_THREAD_POOL_NAME); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java index 1d4907fb891be..606583e83b337 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java @@ -44,7 +44,7 @@ public TransportDeleteLicenseAction( actionFilters, DeleteLicenseRequest::new, indexNameExpressionResolver, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetBasicStatusAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetBasicStatusAction.java index 4ceb3d59a49b2..ec7cc6526ee53 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetBasicStatusAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetBasicStatusAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -38,7 +39,7 @@ public TransportGetBasicStatusAction( GetBasicStatusRequest::new, indexNameExpressionResolver, GetBasicStatusResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java index 3e5daeff20b54..0f6b2b201298d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.protocol.xpack.license.GetLicenseRequest; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -43,7 +44,7 @@ public TransportGetLicenseAction( GetLicenseRequest::new, indexNameExpressionResolver, GetLicenseResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java index efd7c83c58bac..8f2655b0ae64f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetTrialStatusAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -41,7 +42,7 @@ public TransportGetTrialStatusAction( GetTrialStatusRequest::new, indexNameExpressionResolver, GetTrialStatusResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartBasicAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartBasicAction.java index 937084a23b7c2..e3de0a8797e5c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartBasicAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartBasicAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.internal.MutableLicenseService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -42,7 +43,7 @@ public TransportPostStartBasicAction( PostStartBasicRequest::new, indexNameExpressionResolver, PostStartBasicResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java index 08fa8b548da0b..1d3b4a0698ad5 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPostStartTrialAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.internal.MutableLicenseService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -42,7 +43,7 @@ public TransportPostStartTrialAction( PostStartTrialRequest::new, indexNameExpressionResolver, PostStartTrialResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPutLicenseAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPutLicenseAction.java index d4d420ec48c9a..6b9846e636f05 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPutLicenseAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportPutLicenseAction.java @@ -44,7 +44,7 @@ public TransportPutLicenseAction( PutLicenseRequest::new, indexNameExpressionResolver, PutLicenseResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.licenseService = licenseService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/AbstractTransportSetResetModeAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/AbstractTransportSetResetModeAction.java index ad3debee682ef..0d3c45ccedd3d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/AbstractTransportSetResetModeAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/AbstractTransportSetResetModeAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -51,7 +52,7 @@ public AbstractTransportSetResetModeAction( actionFilters, SetResetModeActionRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.clusterService = clusterService; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java index 1fb2664dac007..d67002fba8a7d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/TransportXPackUsageAction.java @@ -49,7 +49,7 @@ public TransportXPackUsageAction( XPackUsageRequest::new, indexNameExpressionResolver, XPackUsageResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.client = client; this.usageActions = usageActions(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureTransportAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureTransportAction.java index ed4fbff6f8a4b..4bf94bcafaa21 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureTransportAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureTransportAction.java @@ -35,7 +35,7 @@ public XPackUsageFeatureTransportAction( XPackUsageRequest::new, indexNameExpressionResolver, XPackUsageFeatureResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoAction.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoAction.java index 1851816e8d143..9aff1c010cac7 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoAction.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoAction.java @@ -70,7 +70,7 @@ public TransportDeprecationInfoAction( DeprecationInfoAction.Request::new, indexNameExpressionResolver, DeprecationInfoAction.Response::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.client = client; this.indexNameExpressionResolver = indexNameExpressionResolver; diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java index 20a3095a2a3ce..87bdd0f0bb8ba 100644 --- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java +++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java @@ -49,6 +49,7 @@ import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.TimeValue; @@ -162,7 +163,7 @@ public TransportDownsampleAction( actionFilters, DownsampleAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = new OriginSettingClient(client, ClientHelper.ROLLUP_ORIGIN); this.indicesService = indicesService; diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportDeleteEnrichPolicyAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportDeleteEnrichPolicyAction.java index 13e98cd6b08c6..bf1327eb8efbe 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportDeleteEnrichPolicyAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportDeleteEnrichPolicyAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.ingest.PipelineConfiguration; import org.elasticsearch.rest.RestStatus; @@ -70,7 +71,7 @@ public TransportDeleteEnrichPolicyAction( actionFilters, DeleteEnrichPolicyAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.enrichPolicyLocks = enrichPolicyLocks; diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportEnrichStatsAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportEnrichStatsAction.java index b4a0fe94e5ce1..02d19bd0e0ff1 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportEnrichStatsAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportEnrichStatsAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -52,7 +53,7 @@ public TransportEnrichStatsAction( EnrichStatsAction.Request::new, indexNameExpressionResolver, EnrichStatsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; } diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportExecuteEnrichPolicyAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportExecuteEnrichPolicyAction.java index 637ba472b0192..c0d447385f228 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportExecuteEnrichPolicyAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportExecuteEnrichPolicyAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -52,7 +53,7 @@ public TransportExecuteEnrichPolicyAction( ExecuteEnrichPolicyAction.Request::new, indexNameExpressionResolver, ExecuteEnrichPolicyAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.executor = enrichPolicyExecutor; } diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportGetEnrichPolicyAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportGetEnrichPolicyAction.java index ea1f20a9e4305..e1f0240b8e8ed 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportGetEnrichPolicyAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportGetEnrichPolicyAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -46,7 +47,7 @@ public TransportGetEnrichPolicyAction( GetEnrichPolicyAction.Request::new, indexNameExpressionResolver, GetEnrichPolicyAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportPutEnrichPolicyAction.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportPutEnrichPolicyAction.java index 39b190f444b5e..2cfc1dc8fffa0 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportPutEnrichPolicyAction.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/action/TransportPutEnrichPolicyAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -56,7 +57,7 @@ public TransportPutEnrichPolicyAction( actionFilters, PutEnrichPolicyAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.settings = settings; this.securityContext = XPackSettings.SECURITY_ENABLED.get(settings) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportDeleteAnalyticsCollectionAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportDeleteAnalyticsCollectionAction.java index fd7396eafce7a..03bf1c2d9adfa 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportDeleteAnalyticsCollectionAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportDeleteAnalyticsCollectionAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public TransportDeleteAnalyticsCollectionAction( actionFilters, DeleteAnalyticsCollectionAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.analyticsCollectionService = analyticsCollectionService; } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportGetAnalyticsCollectionAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportGetAnalyticsCollectionAction.java index 469cf9f36ddeb..41d30017a185b 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportGetAnalyticsCollectionAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportGetAnalyticsCollectionAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public TransportGetAnalyticsCollectionAction( GetAnalyticsCollectionAction.Request::new, indexNameExpressionResolver, GetAnalyticsCollectionAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.analyticsCollectionService = analyticsCollectionService; } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportPutAnalyticsCollectionAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportPutAnalyticsCollectionAction.java index fb6c8fc0fd57e..2f10532b504d4 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportPutAnalyticsCollectionAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/action/TransportPutAnalyticsCollectionAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -45,7 +46,7 @@ public TransportPutAnalyticsCollectionAction( PutAnalyticsCollectionAction.Request::new, indexNameExpressionResolver, PutAnalyticsCollectionAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.analyticsCollectionService = analyticsCollectionService; } diff --git a/x-pack/plugin/frozen-indices/src/main/java/org/elasticsearch/xpack/frozen/action/TransportFreezeIndexAction.java b/x-pack/plugin/frozen-indices/src/main/java/org/elasticsearch/xpack/frozen/action/TransportFreezeIndexAction.java index c95508cba54f2..535fb4c422870 100644 --- a/x-pack/plugin/frozen-indices/src/main/java/org/elasticsearch/xpack/frozen/action/TransportFreezeIndexAction.java +++ b/x-pack/plugin/frozen-indices/src/main/java/org/elasticsearch/xpack/frozen/action/TransportFreezeIndexAction.java @@ -32,6 +32,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; @@ -74,7 +75,7 @@ public TransportFreezeIndexAction( FreezeRequest::new, indexNameExpressionResolver, FreezeResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.destructiveOperations = destructiveOperations; this.indexStateService = indexStateService; diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportDeleteLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportDeleteLifecycleAction.java index 39d87d26c4e95..e222d8f6dd9d8 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportDeleteLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportDeleteLifecycleAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.tasks.Task; @@ -58,7 +59,7 @@ public TransportDeleteLifecycleAction( Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetLifecycleAction.java index 82d029c157348..f58d101a330c8 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetLifecycleAction.java @@ -56,7 +56,7 @@ public TransportGetLifecycleAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetStatusAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetStatusAction.java index 90533405c1fcb..19eac02304835 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetStatusAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportGetStatusAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public TransportGetStatusAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMigrateToDataTiersAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMigrateToDataTiersAction.java index 2003e3ae3d9cb..ae0df89c9bb8f 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMigrateToDataTiersAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMigrateToDataTiersAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.Tuple; import org.elasticsearch.license.XPackLicenseState; @@ -69,7 +70,7 @@ public TransportMigrateToDataTiersAction( MigrateToDataTiersRequest::new, indexNameExpressionResolver, MigrateToDataTiersResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.xContentRegistry = xContentRegistry; this.client = client; diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMoveToStepAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMoveToStepAction.java index 7cce02e35fa27..0774e037fae5a 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMoveToStepAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportMoveToStepAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -55,7 +56,7 @@ public TransportMoveToStepAction( Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexLifecycleService = indexLifecycleService; } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java index 650e4844d1b0b..3b7a242ca5021 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportPutLifecycleAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.metadata.RepositoriesMetadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.XPackLicenseState; @@ -87,7 +88,7 @@ public TransportPutLifecycleAction( Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.xContentRegistry = namedXContentRegistry; this.licenseState = licenseState; diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRemoveIndexLifecyclePolicyAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRemoveIndexLifecyclePolicyAction.java index e1c534782a629..57cc88d24efbc 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRemoveIndexLifecyclePolicyAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRemoveIndexLifecyclePolicyAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; @@ -49,7 +50,7 @@ public TransportRemoveIndexLifecyclePolicyAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRetryAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRetryAction.java index 8233a3455ea82..4a038551b04e0 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRetryAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportRetryAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.LifecycleExecutionState; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -56,7 +57,7 @@ public TransportRetryAction( Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.indexLifecycleService = indexLifecycleService; } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStartILMAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStartILMAction.java index 079016171838c..f725480e2a902 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStartILMAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStartILMAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -45,7 +46,7 @@ public TransportStartILMAction( actionFilters, StartILMRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStopILMAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStopILMAction.java index 05b818da99c18..af457df806569 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStopILMAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportStopILMAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -45,7 +46,7 @@ public TransportStopILMAction( actionFilters, StopILMRequest::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportDeleteInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportDeleteInferenceModelAction.java index 444159b13dcf2..4305ff5a7b631 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportDeleteInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportDeleteInferenceModelAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -43,7 +44,7 @@ public TransportDeleteInferenceModelAction( actionFilters, DeleteInferenceModelAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.modelRegistry = modelRegistry; } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java index 0f35523726656..5b948fc94d36e 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; @@ -58,7 +59,7 @@ public TransportPutInferenceModelAction( PutInferenceModelAction.Request::new, indexNameExpressionResolver, PutInferenceModelAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.modelRegistry = modelRegistry; this.serviceRegistry = serviceRegistry; diff --git a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportGetTrainedModelPackageConfigAction.java b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportGetTrainedModelPackageConfigAction.java index c197f5588d296..41db25881185f 100644 --- a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportGetTrainedModelPackageConfigAction.java +++ b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportGetTrainedModelPackageConfigAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -64,7 +65,7 @@ public TransportGetTrainedModelPackageConfigAction( GetTrainedModelPackageConfigAction.Request::new, indexNameExpressionResolver, GetTrainedModelPackageConfigAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.settings = settings; } diff --git a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportLoadTrainedModelPackage.java b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportLoadTrainedModelPackage.java index 0c787448f5b12..1e4ec69649767 100644 --- a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportLoadTrainedModelPackage.java +++ b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/action/TransportLoadTrainedModelPackage.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; @@ -75,7 +76,7 @@ public TransportLoadTrainedModelPackage( LoadTrainedModelPackageAction.Request::new, indexNameExpressionResolver, NodeAcknowledgedResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = new OriginSettingClient(client, ML_ORIGIN); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCreateTrainedModelAssignmentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCreateTrainedModelAssignmentAction.java index 443184482c978..25f19f9300a19 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCreateTrainedModelAssignmentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCreateTrainedModelAssignmentAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -54,7 +55,7 @@ public TransportCreateTrainedModelAssignmentAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.trainedModelAssignmentClusterService = trainedModelAssignmentClusterService; // Here we create our singleton for the node service diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDataFrameAnalyticsAction.java index 4cee0ba33e9f6..ead2d8cda30b3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDataFrameAnalyticsAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -74,7 +75,7 @@ public TransportDeleteDataFrameAnalyticsAction( actionFilters, DeleteDataFrameAnalyticsAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.memoryTracker = memoryTracker; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDatafeedAction.java index 15707bd40bbe9..64ad51fc0f722 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteDatafeedAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.tasks.Task; @@ -57,7 +58,7 @@ public TransportDeleteDatafeedAction( actionFilters, DeleteDatafeedAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteJobAction.java index 8712c1289349d..6057493c95289 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteJobAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksService; @@ -103,7 +104,7 @@ public TransportDeleteJobAction( actionFilters, DeleteJobAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAction.java index 170e79d5d32d8..093e4213a5db1 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.ingest.IngestMetadata; @@ -92,7 +93,7 @@ public TransportDeleteTrainedModelAction( actionFilters, DeleteTrainedModelAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.trainedModelProvider = configProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAliasAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAliasAction.java index dc1816e940909..73601ef86ff13 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAliasAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAliasAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.ingest.IngestService; @@ -66,7 +67,7 @@ public TransportDeleteTrainedModelAliasAction( actionFilters, DeleteTrainedModelAliasAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.auditor = auditor; this.ingestService = ingestService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAssignmentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAssignmentAction.java index 63204afed66f4..01a58d64b0466 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAssignmentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportDeleteTrainedModelAssignmentAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -46,7 +47,7 @@ public TransportDeleteTrainedModelAssignmentAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.trainedModelAssignmentClusterService = trainedModelAssignmentClusterService; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionAction.java index 9d427b03388fd..80fabff58d101 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -61,7 +62,7 @@ public TransportFinalizeJobExecutionAction( actionFilters, FinalizeJobExecutionAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedsAction.java index 31d2334cf4f1d..ebbe06e69ba63 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetDatafeedsAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.threadpool.ThreadPool; @@ -48,7 +49,7 @@ public TransportGetDatafeedsAction( GetDatafeedsAction.Request::new, indexNameExpressionResolver, GetDatafeedsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.datafeedManager = datafeedManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobModelSnapshotsUpgradeStatsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobModelSnapshotsUpgradeStatsAction.java index d78d4cf6f6587..5ceb015198df5 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobModelSnapshotsUpgradeStatsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobModelSnapshotsUpgradeStatsAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -66,7 +67,7 @@ public TransportGetJobModelSnapshotsUpgradeStatsAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.jobConfigProvider = jobConfigProvider; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsAction.java index 34da64aa5a5e0..0761de41bbd21 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetJobsAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.threadpool.ThreadPool; @@ -56,7 +57,7 @@ public TransportGetJobsAction( GetJobsAction.Request::new, indexNameExpressionResolver, GetJobsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.jobManager = jobManager; this.datafeedManager = datafeedManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetMlAutoscalingStats.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetMlAutoscalingStats.java index 7ce5fd0a66eb2..11deac3e93f8c 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetMlAutoscalingStats.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportGetMlAutoscalingStats.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; @@ -65,7 +66,7 @@ public TransportGetMlAutoscalingStats( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.mlMemoryTracker = mlMemoryTracker; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlMemoryAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlMemoryAction.java index 08c6361c09408..ff468a9fcac83 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlMemoryAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlMemoryAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.monitor.os.OsStats; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; @@ -73,7 +74,7 @@ public TransportMlMemoryAction( MlMemoryAction.Request::new, indexNameExpressionResolver, MlMemoryAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = new OriginSettingClient(client, ML_ORIGIN); this.memoryTracker = memoryTracker; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java index 5e033278242ad..c527c00a738a2 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; @@ -101,7 +102,7 @@ public TransportOpenJobAction( OpenJobAction.Request::new, indexNameExpressionResolver, NodeAcknowledgedResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDataFrameAnalyticsAction.java index 9ae73224a698d..d73b942e766cf 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDataFrameAnalyticsAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.license.License; import org.elasticsearch.license.LicenseUtils; @@ -99,7 +100,7 @@ public TransportPutDataFrameAnalyticsAction( PutDataFrameAnalyticsAction.Request::new, indexNameExpressionResolver, PutDataFrameAnalyticsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.configProvider = configProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDatafeedAction.java index 7d19e1f6fdd15..d71e99040177f 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutDatafeedAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.Task; @@ -54,7 +55,7 @@ public TransportPutDatafeedAction( PutDatafeedAction.Request::new, indexNameExpressionResolver, PutDatafeedAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.securityContext = XPackSettings.SECURITY_ENABLED.get(settings) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutJobAction.java index fe824571d3f9e..ebe766f6b5669 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutJobAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; @@ -70,7 +71,7 @@ public TransportPutJobAction( PutJobAction.Request::new, indexNameExpressionResolver, PutJobAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.jobManager = jobManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java index 8005bc6a000c3..6f97689222196 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.query.QueryBuilder; @@ -117,7 +118,7 @@ public TransportPutTrainedModelAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.trainedModelProvider = trainedModelProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAliasAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAliasAction.java index f01ba02892a43..de760d8fa17ed 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAliasAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAliasAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.logging.HeaderWarning; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.License; @@ -84,7 +85,7 @@ public TransportPutTrainedModelAliasAction( actionFilters, PutTrainedModelAliasAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.trainedModelProvider = trainedModelProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelVocabularyAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelVocabularyAction.java index 914824512e36c..57b5e269aa4c7 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelVocabularyAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelVocabularyAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.rest.RestStatus; @@ -57,7 +58,7 @@ public TransportPutTrainedModelVocabularyAction( Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.trainedModelProvider = trainedModelProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportResetJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportResetJobAction.java index 035b63c44649f..35a80876ea763 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportResetJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportResetJobAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.CancellableTask; @@ -80,7 +81,7 @@ public TransportResetJobAction( actionFilters, ResetJobAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = Objects.requireNonNull(client); this.jobConfigProvider = Objects.requireNonNull(jobConfigProvider); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportRevertModelSnapshotAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportRevertModelSnapshotAction.java index b12d1ffd75287..81287ce749d83 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportRevertModelSnapshotAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportRevertModelSnapshotAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -83,7 +84,7 @@ public TransportRevertModelSnapshotAction( RevertModelSnapshotAction.Request::new, indexNameExpressionResolver, RevertModelSnapshotAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.client = client; this.jobManager = jobManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportSetUpgradeModeAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportSetUpgradeModeAction.java index 8279c90b9de24..07b556cf9a989 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportSetUpgradeModeAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportSetUpgradeModeAction.java @@ -27,6 +27,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.persistent.PersistentTasksClusterService; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -86,7 +87,7 @@ public TransportSetUpgradeModeAction( actionFilters, SetUpgradeModeAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.persistentTasksClusterService = persistentTasksClusterService; this.clusterService = clusterService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java index d8f1bbf9389c9..2a1844ea1fccf 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.license.License; @@ -136,7 +137,7 @@ public TransportStartDataFrameAnalyticsAction( StartDataFrameAnalyticsAction.Request::new, indexNameExpressionResolver, NodeAcknowledgedResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.client = client; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java index 66ad07e76bca1..38b827b120bf6 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; @@ -127,7 +128,7 @@ public TransportStartDatafeedAction( StartDatafeedAction.Request::new, indexNameExpressionResolver, NodeAcknowledgedResponse::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartTrainedModelDeploymentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartTrainedModelDeploymentAction.java index 795b67fb19aca..113a093b3ae65 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartTrainedModelDeploymentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartTrainedModelDeploymentAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.query.QueryBuilders; @@ -116,7 +117,7 @@ public TransportStartTrainedModelDeploymentAction( StartTrainedModelDeploymentAction.Request::new, indexNameExpressionResolver, CreateTrainedModelAssignmentAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = Objects.requireNonNull(licenseState); this.client = new OriginSettingClient(Objects.requireNonNull(client), ML_ORIGIN); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDataFrameAnalyticsAction.java index 921adf3924dde..66ce0df432c6c 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDataFrameAnalyticsAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.Task; @@ -66,7 +67,7 @@ public TransportUpdateDataFrameAnalyticsAction( UpdateDataFrameAnalyticsAction.Request::new, indexNameExpressionResolver, PutDataFrameAnalyticsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.configProvider = configProvider; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDatafeedAction.java index 3b74b751dc705..a71926a868c85 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateDatafeedAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -49,7 +50,7 @@ public TransportUpdateDatafeedAction( UpdateDatafeedAction.Request::new, indexNameExpressionResolver, PutDatafeedAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.datafeedManager = datafeedManager; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java index 9f511c027abd6..3781bdca41237 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public TransportUpdateJobAction( UpdateJobAction.Request::new, indexNameExpressionResolver, PutJobAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.jobManager = jobManager; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelAssignmentStateAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelAssignmentStateAction.java index 31558ad127bd8..900e0e5aa2a43 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelAssignmentStateAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelAssignmentStateAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -46,7 +47,7 @@ public TransportUpdateTrainedModelAssignmentStateAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.trainedModelAssignmentClusterService = trainedModelAssignmentClusterService; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelDeploymentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelDeploymentAction.java index 9c1578878d232..7d4143d9e722a 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelDeploymentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateTrainedModelDeploymentAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -59,7 +60,7 @@ public TransportUpdateTrainedModelDeploymentAction( UpdateTrainedModelDeploymentAction.Request::new, indexNameExpressionResolver, CreateTrainedModelAssignmentAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.trainedModelAssignmentClusterService = Objects.requireNonNull(trainedModelAssignmentClusterService); this.auditor = Objects.requireNonNull(auditor); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpgradeJobModelSnapshotAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpgradeJobModelSnapshotAction.java index bd6b55d10af5a..6335e0b78bd83 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpgradeJobModelSnapshotAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpgradeJobModelSnapshotAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; @@ -88,7 +89,7 @@ public TransportUpgradeJobModelSnapshotAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.licenseState = licenseState; this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionActionTests.java index c8096a7b1f384..1c0243fcab098 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionActionTests.java @@ -81,12 +81,8 @@ public void testOperation() { } private TransportFinalizeJobExecutionAction createAction(ClusterService clusterService) { - // TODO: temporary, remove in #97879 - TransportService transportService = mock(TransportService.class); - when(transportService.getThreadPool()).thenReturn(threadPool); - return new TransportFinalizeJobExecutionAction( - transportService, + mock(TransportService.class), clusterService, threadPool, mock(ActionFilters.class), diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringMigrateAlertsAction.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringMigrateAlertsAction.java index 289993d1abc9d..8f276829e242b 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringMigrateAlertsAction.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/action/TransportMonitoringMigrateAlertsAction.java @@ -74,7 +74,7 @@ public TransportMonitoringMigrateAlertsAction( MonitoringMigrateAlertsRequest::new, indexNameExpressionResolver, MonitoringMigrateAlertsResponse::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.client = client; this.migrationCoordinator = migrationCoordinator; diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStatusAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStatusAction.java index 8110cc5e968ec..6c4a7d455539f 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStatusAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStatusAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.node.NodeClosedException; import org.elasticsearch.tasks.Task; @@ -51,7 +52,7 @@ public TransportGetStatusAction( GetStatusAction.Request::new, indexNameExpressionResolver, GetStatusAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.resolver = new StatusResolver(clusterService); } diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java index 29fa367bdc129..9364d5fcc3f6d 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java @@ -36,6 +36,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.time.DateUtils; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.TimeValue; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -86,7 +87,7 @@ public TransportPutRollupJobAction( actionFilters, PutRollupJobAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.persistentTasksService = persistentTasksService; this.client = client; diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java index 8d1fa88826279..59d6e7a5feac3 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java @@ -99,7 +99,7 @@ public TransportMountSearchableSnapshotAction( indexNameExpressionResolver, RestoreSnapshotResponse::new, // Use SNAPSHOT_META pool since we are slow due to loading repository metadata in this action - ThreadPool.Names.SNAPSHOT_META + threadPool.executor(ThreadPool.Names.SNAPSHOT_META) ); this.client = client; this.repositoriesService = repositoriesService; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportGetSecuritySettingsAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportGetSecuritySettingsAction.java index b2b66004cb893..8b883b01bd16f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportGetSecuritySettingsAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportGetSecuritySettingsAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -54,7 +55,7 @@ public TransportGetSecuritySettingsAction( GetSecuritySettingsAction.Request::new, indexNameExpressionResolver, GetSecuritySettingsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportUpdateSecuritySettingsAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportUpdateSecuritySettingsAction.java index 01b52454de6b1..279697ac1eb4f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportUpdateSecuritySettingsAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/settings/TransportUpdateSecuritySettingsAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.Index; import org.elasticsearch.tasks.Task; @@ -67,7 +68,7 @@ public TransportUpdateSecuritySettingsAction( UpdateSecuritySettingsAction.Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.updateSettingsService = metadataUpdateSettingsService; } diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportDeleteShutdownNodeAction.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportDeleteShutdownNodeAction.java index 15250f0d8f6f8..3e0e578ded120 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportDeleteShutdownNodeAction.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportDeleteShutdownNodeAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.cluster.service.MasterServiceTaskQueue; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -120,7 +121,7 @@ public TransportDeleteShutdownNodeAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); taskQueue = clusterService.createTaskQueue("delete-node-shutdown", Priority.URGENT, new DeleteShutdownNodeExecutor()); } diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportGetShutdownStatusAction.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportGetShutdownStatusAction.java index 425db500070e5..29c7f1b98f4bf 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportGetShutdownStatusAction.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportGetShutdownStatusAction.java @@ -91,7 +91,7 @@ public TransportGetShutdownStatusAction( GetShutdownStatusAction.Request::readFrom, indexNameExpressionResolver, GetShutdownStatusAction.Response::new, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); this.allocationService = allocationService; this.allocationDeciders = allocationDeciders; diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportPutShutdownNodeAction.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportPutShutdownNodeAction.java index ad5c4bef13fbc..767c128030538 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportPutShutdownNodeAction.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/TransportPutShutdownNodeAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.cluster.service.MasterServiceTaskQueue; import org.elasticsearch.common.Priority; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -168,7 +169,7 @@ public TransportPutShutdownNodeAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); taskQueue = clusterService.createTaskQueue("put-shutdown", Priority.URGENT, new PutShutdownNodeExecutor()); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportDeleteSnapshotLifecycleAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportDeleteSnapshotLifecycleAction.java index 258346b59dca7..3b2dc9e23d172 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportDeleteSnapshotLifecycleAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportDeleteSnapshotLifecycleAction.java @@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.tasks.Task; @@ -57,7 +58,7 @@ public TransportDeleteSnapshotLifecycleAction( DeleteSnapshotLifecycleAction.Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotLifecycleAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotLifecycleAction.java index f7358cfa939f4..6eecba174a8a3 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotLifecycleAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotLifecycleAction.java @@ -56,7 +56,7 @@ public TransportExecuteSnapshotLifecycleAction( ExecuteSnapshotLifecycleAction.Request::new, indexNameExpressionResolver, ExecuteSnapshotLifecycleAction.Response::new, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.client = client; this.historyStore = historyStore; diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotRetentionAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotRetentionAction.java index 7adb58bcf8a8c..745f8af4b98a0 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotRetentionAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportExecuteSnapshotRetentionAction.java @@ -49,7 +49,7 @@ public TransportExecuteSnapshotRetentionAction( actionFilters, ExecuteSnapshotRetentionAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.GENERIC + threadPool.executor(ThreadPool.Names.GENERIC) ); this.retentionService = retentionService; } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSLMStatusAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSLMStatusAction.java index 57b585395edc2..ddb78bd6c5053 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSLMStatusAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSLMStatusAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -42,7 +43,7 @@ public TransportGetSLMStatusAction( GetSLMStatusAction.Request::new, indexNameExpressionResolver, GetSLMStatusAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleAction.java index 32a9d127b8363..8cecf9c0f76d1 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -56,7 +57,7 @@ public TransportGetSnapshotLifecycleAction( GetSnapshotLifecycleAction.Request::new, indexNameExpressionResolver, GetSnapshotLifecycleAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleStatsAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleStatsAction.java index 574bb9d6ea6c0..d601c6bc2afb2 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleStatsAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportGetSnapshotLifecycleStatsAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -44,7 +45,7 @@ public TransportGetSnapshotLifecycleStatsAction( GetSnapshotLifecycleStatsAction.Request::new, indexNameExpressionResolver, GetSnapshotLifecycleStatsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportPutSnapshotLifecycleAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportPutSnapshotLifecycleAction.java index 7fd2da5c8661b..1e7f58b02e2ac 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportPutSnapshotLifecycleAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportPutSnapshotLifecycleAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.tasks.Task; @@ -66,7 +67,7 @@ public TransportPutSnapshotLifecycleAction( PutSnapshotLifecycleAction.Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStartSLMAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStartSLMAction.java index 15b6f29d1796a..48cd7ff0309bd 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStartSLMAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStartSLMAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -44,7 +45,7 @@ public TransportStartSLMAction( actionFilters, StartSLMAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStopSLMAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStopSLMAction.java index bfaa3f15f56c8..5f0eaa6cb90cb 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStopSLMAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/action/TransportStopSLMAction.java @@ -18,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -44,7 +45,7 @@ public TransportStopSLMAction( actionFilters, StopSLMAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java index 9e94fbb3e5de9..1c2f83c38a38e 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportDeleteTransformAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexNotFoundException; @@ -70,7 +71,7 @@ public TransportDeleteTransformAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.transformConfigManager = transformServices.getConfigManager(); this.auditor = transformServices.getAuditor(); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPutTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPutTransformAction.java index 94ec3f702554e..4c86aed335ac1 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPutTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPutTransformAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -79,7 +80,7 @@ public TransportPutTransformAction( actionFilters, PutTransformAction.Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.settings = settings; this.client = client; diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportResetTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportResetTransformAction.java index bff969cf0b856..e597254bfe713 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportResetTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportResetTransformAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; @@ -81,7 +82,7 @@ public TransportResetTransformAction( actionFilters, Request::new, indexNameExpressionResolver, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.transformConfigManager = transformServices.getConfigManager(); this.auditor = transformServices.getAuditor(); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportStartTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportStartTransformAction.java index 8776f112e6178..375c6e063ac6d 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportStartTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportStartTransformAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.health.HealthStatus; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -113,7 +114,7 @@ protected TransportStartTransformAction( StartTransformAction.Request::new, indexNameExpressionResolver, StartTransformAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.transformConfigManager = transformServices.getConfigManager(); this.persistentTasksService = persistentTasksService; diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportUpgradeTransformsAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportUpgradeTransformsAction.java index 6b01f6d7966a0..62ac44a2f6191 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportUpgradeTransformsAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportUpgradeTransformsAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; @@ -80,7 +81,7 @@ public TransportUpgradeTransformsAction( Request::new, indexNameExpressionResolver, Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.transformConfigManager = transformServices.getConfigManager(); this.settings = settings; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportGetWatcherSettingsAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportGetWatcherSettingsAction.java index f42f9c581763f..eecbc21ad0475 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportGetWatcherSettingsAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportGetWatcherSettingsAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; @@ -49,7 +50,7 @@ public TransportGetWatcherSettingsAction( GetWatcherSettingsAction.Request::new, indexNameExpressionResolver, GetWatcherSettingsAction.Response::new, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); } diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportUpdateWatcherSettingsAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportUpdateWatcherSettingsAction.java index f55ae6c95eb25..124cb6de5fd40 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportUpdateWatcherSettingsAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportUpdateWatcherSettingsAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.Index; import org.elasticsearch.logging.LogManager; @@ -59,7 +60,7 @@ public TransportUpdateWatcherSettingsAction( UpdateWatcherSettingsAction.Request::new, indexNameExpressionResolver, AcknowledgedResponse::readFrom, - ThreadPool.Names.SAME + EsExecutors.DIRECT_EXECUTOR_SERVICE ); this.updateSettingsService = updateSettingsService; } diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportWatcherServiceAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportWatcherServiceAction.java index bfc0244e5f13f..4d7612d843379 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportWatcherServiceAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/TransportWatcherServiceAction.java @@ -55,7 +55,7 @@ public TransportWatcherServiceAction( actionFilters, WatcherServiceRequest::new, indexNameExpressionResolver, - ThreadPool.Names.MANAGEMENT + threadPool.executor(ThreadPool.Names.MANAGEMENT) ); } From f1c4fb2c781b11191e3cb8d01e3c754ef39db9c8 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 26 Sep 2023 18:48:31 +0100 Subject: [PATCH 084/155] [ML] Decouple ML template versioning from product version (#99921) The ML index templates used to be versioned using the product version. This won't work in serverless, so needs to be changed. Most plugins have a separate version constant for their index templates that started at 1 and is incremented manually when any template changes. For ML, we've got used to _not_ having to worry about index template versions, only mappings versions. To try to continue this approach as far as possible, the new strategy for ML index template versions is: - Start with 10000000. This is because 8.11.0's ID is 8110099, and whatever new scheme we use must generate numbers bigger than this. - Add on the mappings version constants for all index mappings for which we use index templates. This means that incrementing the mappings version is still sufficient to cause the templates to be reinstalled. It's only necessary to increment the base template version if something changes in a template that's _not_ in the mappings, such as priority. This should be very rare. So the risk of forgetting to update the template versions when updating mappings is removed. --- .../ml/notifications/NotificationsIndex.java | 2 +- .../xpack/core/template/TemplateUtils.java | 5 +- .../notifications/AbstractAuditorTests.java | 14 ++-- .../core/ml/utils/MlIndexAndAliasTests.java | 68 +++++++------------ .../xpack/ml/MachineLearning.java | 7 +- .../xpack/ml/MlIndexTemplateRegistry.java | 34 +++++++--- .../xpack/test/rest/XPackRestTestHelper.java | 36 +--------- 7 files changed, 65 insertions(+), 101 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java index 06a923cd9d275..08f493fd29523 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java @@ -17,7 +17,7 @@ public final class NotificationsIndex { private static final String RESOURCE_PATH = "/ml/"; private static final String MAPPINGS_VERSION_VARIABLE = "xpack.ml.version"; - private static final int NOTIFICATIONS_INDEX_MAPPINGS_VERSION = 1; + public static final int NOTIFICATIONS_INDEX_MAPPINGS_VERSION = 1; private NotificationsIndex() {} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/TemplateUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/TemplateUtils.java index 895305d9372b8..ad27607e47c5e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/TemplateUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/template/TemplateUtils.java @@ -117,14 +117,15 @@ public static String replaceVariable(String input, String variable, String value * Checks if a versioned template exists, and if it exists checks if the version is greater than or equal to the current version. * @param templateName Name of the index template * @param state Cluster state + * @param currentVersion The current version to check against */ - public static boolean checkTemplateExistsAndVersionIsGTECurrentVersion(String templateName, ClusterState state) { + public static boolean checkTemplateExistsAndVersionIsGTECurrentVersion(String templateName, ClusterState state, long currentVersion) { ComposableIndexTemplate templateMetadata = state.metadata().templatesV2().get(templateName); if (templateMetadata == null) { return false; } - return templateMetadata.version() != null && templateMetadata.version() >= Version.CURRENT.id; + return templateMetadata.version() != null && templateMetadata.version() >= currentVersion; } /** diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/notifications/AbstractAuditorTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/notifications/AbstractAuditorTests.java index babf57d41a85e..cfd5b4cd381c5 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/notifications/AbstractAuditorTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/notifications/AbstractAuditorTests.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.core.common.notifications; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; import org.elasticsearch.action.bulk.BulkAction; @@ -22,7 +21,6 @@ import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.metadata.Metadata; -import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; @@ -69,6 +67,8 @@ public class AbstractAuditorTests extends ESTestCase { private static final String TEST_ORIGIN = "test_origin"; private static final String TEST_INDEX = "test_index"; + private static final int TEST_TEMPLATE_VERSION = 23456789; + private Client client; private ArgumentCaptor indexRequestCaptor; private long startMillis; @@ -231,11 +231,8 @@ private TestAuditor createTestAuditorWithTemplateInstalled() { Metadata metadata = mock(Metadata.class); when(metadata.getTemplates()).thenReturn(templates); when(metadata.templatesV2()).thenReturn(templatesV2); - DiscoveryNodes nodes = mock(DiscoveryNodes.class); - when(nodes.getMinNodeVersion()).thenReturn(Version.CURRENT); ClusterState state = mock(ClusterState.class); when(state.getMetadata()).thenReturn(metadata); - when(state.nodes()).thenReturn(nodes); ClusterService clusterService = mock(ClusterService.class); when(clusterService.state()).thenReturn(state); @@ -274,11 +271,8 @@ private TestAuditor createTestAuditorWithoutTemplate(CountDownLatch latch) { Metadata metadata = mock(Metadata.class); when(metadata.getTemplates()).thenReturn(Map.of()); - DiscoveryNodes nodes = mock(DiscoveryNodes.class); - when(nodes.getMinNodeVersion()).thenReturn(Version.CURRENT); ClusterState state = mock(ClusterState.class); when(state.getMetadata()).thenReturn(metadata); - when(state.nodes()).thenReturn(nodes); ClusterService clusterService = mock(ClusterService.class); when(clusterService.state()).thenReturn(state); @@ -294,11 +288,11 @@ public static class TestAuditor extends AbstractAuditor existingIndexNames, String expectedWriteIndexName) - throws UnknownHostException { + private void assertMlStateWriteAliasAddedToMostRecentMlStateIndex(List existingIndexNames, String expectedWriteIndexName) { ClusterState clusterState = createClusterState( existingIndexNames.stream().collect(toMap(Function.identity(), MlIndexAndAliasTests::createIndexMetadata)) ); @@ -328,23 +320,22 @@ private void assertMlStateWriteAliasAddedToMostRecentMlStateIndex(List e ); } - public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButInitialStateIndexExists() throws UnknownHostException { + public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButInitialStateIndexExists() { assertMlStateWriteAliasAddedToMostRecentMlStateIndex(Arrays.asList(FIRST_CONCRETE_INDEX), FIRST_CONCRETE_INDEX); } - public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButSubsequentStateIndicesExist() throws UnknownHostException { + public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButSubsequentStateIndicesExist() { assertMlStateWriteAliasAddedToMostRecentMlStateIndex(Arrays.asList("test-000003", "test-000040", "test-000500"), "test-000500"); } - public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButBothLegacyAndNewIndicesExist() - throws UnknownHostException { + public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButBothLegacyAndNewIndicesExist() { assertMlStateWriteAliasAddedToMostRecentMlStateIndex( Arrays.asList(LEGACY_INDEX_WITHOUT_SUFFIX, "test-000003", "test-000040", "test-000500"), "test-000500" ); } - public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButLegacyStateIndexExists() throws UnknownHostException { + public void testCreateStateIndexAndAliasIfNecessary_WriteAliasDoesNotExistButLegacyStateIndexExists() { ClusterState clusterState = createClusterState( Collections.singletonMap(LEGACY_INDEX_WITHOUT_SUFFIX, createIndexMetadata(LEGACY_INDEX_WITHOUT_SUFFIX)) ); @@ -392,25 +383,16 @@ private static Answer withResponse(Response response) { }; } - private static ClusterState createClusterState(Map indices) throws UnknownHostException { - return createClusterState(Version.CURRENT, indices, Collections.emptyMap(), Collections.emptyMap()); + private static ClusterState createClusterState(Map indices) { + return createClusterState(indices, Collections.emptyMap(), Collections.emptyMap()); } private static ClusterState createClusterState( - Version minNodeVersion, Map indices, Map legacyTemplates, Map composableTemplates - ) throws UnknownHostException { - InetAddress inetAddress1 = InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, (byte) 0, (byte) 1 }); - InetAddress inetAddress2 = InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, (byte) 0, (byte) 2 }); + ) { return ClusterState.builder(ClusterName.DEFAULT) - .nodes( - DiscoveryNodes.builder() - .add(DiscoveryNodeUtils.create("foo", new TransportAddress(inetAddress1, 9201))) - .add(DiscoveryNodeUtils.create("bar", new TransportAddress(inetAddress2, 9202), minNodeVersion)) - .build() - ) .metadata(Metadata.builder().indices(indices).templates(legacyTemplates).indexTemplates(composableTemplates).build()) .build(); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index d527f998a1ee8..c8e4fd488394a 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -1752,7 +1752,12 @@ public static boolean criticalTemplatesInstalled(ClusterState clusterState) { // installs it if necessary List templateNames = List.of(STATE_INDEX_PREFIX, AnomalyDetectorsIndex.jobResultsIndexPrefix()); for (String templateName : templateNames) { - allPresent = allPresent && TemplateUtils.checkTemplateExistsAndVersionIsGTECurrentVersion(templateName, clusterState); + allPresent = allPresent + && TemplateUtils.checkTemplateExistsAndVersionIsGTECurrentVersion( + templateName, + clusterState, + MlIndexTemplateRegistry.ML_INDEX_TEMPLATE_VERSION + ); } return allPresent; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexTemplateRegistry.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexTemplateRegistry.java index 0d1ba0d3fece1..91e738bf2183b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexTemplateRegistry.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexTemplateRegistry.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.ml; -import org.elasticsearch.Version; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.service.ClusterService; @@ -29,6 +28,23 @@ public class MlIndexTemplateRegistry extends IndexTemplateRegistry { + /** + * The template version starts from 10000000 because up until 8.11.0 we + * used version IDs for template versioning, so the first detached + * version number needs to be higher than the version ID of 8.11.0. + * We add on the mappings version of each of the templates that has + * mappings. This will cause _all_ templates to get installed when the + * mappings for any single template change. However, this is better + * than the risk of potentially updating mappings without updating the + * template versions and hence not reinstalling the templates. Note that + * the state index has no mappings - its template basically just says + * this - hence there's no mappings version for the state index. Please + * add a comment with a reason each time the base number is incremented. + * 10000001: TODO - reason + */ + public static final int ML_INDEX_TEMPLATE_VERSION = 10000000 + AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION + + NotificationsIndex.NOTIFICATIONS_INDEX_MAPPINGS_VERSION + MlStatsIndex.STATS_INDEX_MAPPINGS_VERSION; + private static final String ROOT_RESOURCE_PATH = "/ml/"; private static final String ANOMALY_DETECTION_PATH = ROOT_RESOURCE_PATH + "anomalydetection/"; private static final String VERSION_PATTERN = "xpack.ml.version"; @@ -42,7 +58,7 @@ public class MlIndexTemplateRegistry extends IndexTemplateRegistry { private IndexTemplateConfig stateTemplate() { Map variables = new HashMap<>(); - variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id)); + variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION)); // In serverless a different version of "state_index_template.json" is shipped that won't substitute the ILM policy variable variables.put(INDEX_LIFECYCLE_NAME, ML_SIZE_BASED_ILM_POLICY_NAME); variables.put(INDEX_LIFECYCLE_ROLLOVER_ALIAS, AnomalyDetectorsIndex.jobStateIndexWriteAlias()); @@ -50,7 +66,7 @@ private IndexTemplateConfig stateTemplate() { return new IndexTemplateConfig( AnomalyDetectorsIndexFields.STATE_INDEX_PREFIX, ANOMALY_DETECTION_PATH + "state_index_template.json", - Version.CURRENT.id, + ML_INDEX_TEMPLATE_VERSION, VERSION_PATTERN, variables ); @@ -58,13 +74,13 @@ private IndexTemplateConfig stateTemplate() { private static IndexTemplateConfig anomalyDetectionResultsTemplate() { Map variables = new HashMap<>(); - variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id)); + variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION)); variables.put("xpack.ml.anomalydetection.results.mappings", AnomalyDetectorsIndex.resultsMapping()); return new IndexTemplateConfig( AnomalyDetectorsIndex.jobResultsIndexPrefix(), ANOMALY_DETECTION_PATH + "results_index_template.json", - Version.CURRENT.id, + ML_INDEX_TEMPLATE_VERSION, VERSION_PATTERN, variables ); @@ -72,13 +88,13 @@ private static IndexTemplateConfig anomalyDetectionResultsTemplate() { private static IndexTemplateConfig notificationsTemplate() { Map variables = new HashMap<>(); - variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id)); + variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION)); variables.put("xpack.ml.notifications.mappings", NotificationsIndex.mapping()); return new IndexTemplateConfig( NotificationsIndex.NOTIFICATIONS_INDEX, ROOT_RESOURCE_PATH + "notifications_index_template.json", - Version.CURRENT.id, + ML_INDEX_TEMPLATE_VERSION, VERSION_PATTERN, variables ); @@ -86,7 +102,7 @@ private static IndexTemplateConfig notificationsTemplate() { private IndexTemplateConfig statsTemplate() { Map variables = new HashMap<>(); - variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id)); + variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION)); variables.put("xpack.ml.stats.mappings", MlStatsIndex.mapping()); // In serverless a different version of "stats_index_template.json" is shipped that won't substitute the ILM policy variable variables.put(INDEX_LIFECYCLE_NAME, ML_SIZE_BASED_ILM_POLICY_NAME); @@ -95,7 +111,7 @@ private IndexTemplateConfig statsTemplate() { return new IndexTemplateConfig( MlStatsIndex.TEMPLATE_NAME, ROOT_RESOURCE_PATH + "stats_index_template.json", - Version.CURRENT.id, + ML_INDEX_TEMPLATE_VERSION, VERSION_PATTERN, variables ); diff --git a/x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestHelper.java b/x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestHelper.java index e372578d5924a..31b74b8706877 100644 --- a/x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestHelper.java +++ b/x-pack/qa/src/main/java/org/elasticsearch/xpack/test/rest/XPackRestTestHelper.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.test.rest; import org.apache.http.util.EntityUtils; -import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.RestClient; import org.elasticsearch.common.xcontent.XContentHelper; @@ -18,12 +17,9 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static org.elasticsearch.test.ESTestCase.assertBusy; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.fail; public final class XPackRestTestHelper { @@ -31,8 +27,7 @@ public final class XPackRestTestHelper { private XPackRestTestHelper() {} /** - * For each template name wait for the template to be created and - * for the template version to be equal to the master node version. + * For each template name wait for the template to be created. * * @param client The rest client * @param expectedTemplates Names of the templates to wait for @@ -41,23 +36,6 @@ private XPackRestTestHelper() {} @SuppressWarnings("unchecked") public static void waitForTemplates(RestClient client, List expectedTemplates, boolean clusterUnderstandsComposableTemplates) throws Exception { - AtomicReference masterNodeVersion = new AtomicReference<>(); - - assertBusy(() -> { - Request request = new Request("GET", "/_cat/nodes"); - request.addParameter("h", "master,version"); - request.addParameter("error_trace", "true"); - String response = EntityUtils.toString(client.performRequest(request).getEntity()); - - for (String line : response.split("\n")) { - if (line.startsWith("*")) { - masterNodeVersion.set(Version.fromString(line.substring(2).trim())); - return; - } - } - fail("No master elected"); - }); - // TODO: legacy support can be removed once all X-Pack plugins use only composable // templates in the oldest version we test upgrades from assertBusy(() -> { @@ -102,18 +80,6 @@ public static void waitForTemplates(RestClient client, List expectedTemp + legacyTemplates ); } - - expectedTemplates.forEach(template -> { - Map templateDefinition = (Map) response.get(template); - if (templateDefinition == null) { - templateDefinition = (Map) legacyResponse.get(template); - } - assertThat( - "Template [" + template + "] has unexpected version", - Version.fromId((Integer) templateDefinition.get("version")), - equalTo(masterNodeVersion.get()) - ); - }); }); } From c3f164a863870a91faeb85d4ff34116d4ed2cb89 Mon Sep 17 00:00:00 2001 From: Dianna Hohensee Date: Tue, 26 Sep 2023 15:40:53 -0400 Subject: [PATCH 085/155] Remove temporary TransportAction constructors (#99928) Temporary constructors were added for serverless compatibility until serverless could be updated to use new constructors. Closes #97879 --- .../support/HandledTransportAction.java | 13 --------- .../TransportBroadcastUnpromotableAction.java | 23 ---------------- .../master/TransportMasterNodeAction.java | 27 ------------------- .../support/nodes/TransportNodesAction.java | 16 ----------- 4 files changed, 79 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java b/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java index 9255ea5daddb2..ac7c1d2222b47 100644 --- a/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/HandledTransportAction.java @@ -44,19 +44,6 @@ protected HandledTransportAction( this(actionName, true, transportService, actionFilters, requestReader, executor); } - /** - * Temporary for serverless compatibility. TODO remove. - */ - protected HandledTransportAction( - String actionName, - TransportService transportService, - ActionFilters actionFilters, - Writeable.Reader requestReader, - String executor - ) { - this(actionName, true, transportService, actionFilters, requestReader, transportService.getThreadPool().executor(executor)); - } - protected HandledTransportAction( String actionName, boolean canTripCircuitBreaker, diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java index 75ba30e63763b..4b360a3e2f7d1 100644 --- a/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/unpromotable/TransportBroadcastUnpromotableAction.java @@ -44,29 +44,6 @@ public abstract class TransportBroadcastUnpromotableAction requestReader, - String executor - ) { - this( - actionName, - clusterService, - transportService, - shardStateAction, - actionFilters, - requestReader, - transportService.getThreadPool().executor(executor) - ); - } - protected TransportBroadcastUnpromotableAction( String actionName, ClusterService clusterService, diff --git a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java index c14d8adb04e46..41d817c22be8f 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/TransportMasterNodeAction.java @@ -67,33 +67,6 @@ public abstract class TransportMasterNodeAction request, - IndexNameExpressionResolver indexNameExpressionResolver, - Writeable.Reader response, - String executor - ) { - this( - actionName, - transportService, - clusterService, - threadPool, - actionFilters, - request, - indexNameExpressionResolver, - response, - threadPool.executor(executor) - ); - } - protected TransportMasterNodeAction( String actionName, TransportService transportService, diff --git a/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java b/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java index 3a4b7fb0fa3bb..e974e15966ce9 100644 --- a/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/nodes/TransportNodesAction.java @@ -56,22 +56,6 @@ public abstract class TransportNodesAction< private final Executor finalExecutor; - /** - * Temporary for serverless compatibility. TODO remove. - */ - protected TransportNodesAction( - String actionName, - ThreadPool threadPool, - ClusterService clusterService, - TransportService transportService, - ActionFilters actionFilters, - Writeable.Reader request, - Writeable.Reader nodeRequest, - String executor - ) { - this(actionName, threadPool, clusterService, transportService, actionFilters, request, nodeRequest, threadPool.executor(executor)); - } - /** * @param actionName action name * @param threadPool thread-pool From 09f999c05511c3ef963917283b25a547bc15a38e Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Tue, 26 Sep 2023 17:02:54 -0500 Subject: [PATCH 086/155] Excluding ilm history from a data stream query in a yaml rest test (#99932) In #99837 we stopped setting `indices.lifecycle.history_index_enabled` to false in the data streams yaml rest tests. I missed a place where we do a wildcard datastream query that can include hidden indices. This PR corrects that mistake. I searched all the .yml files and couldn't find any other places we do a wildcard query that could match the ilm history index. Closes #99913 Closes #99889 --- .../resources/rest-api-spec/test/data_stream/10_basic.yml | 2 +- .../test/data_stream/80_resolve_index_data_streams.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml index d7e163e20c018..0c2279608c316 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml @@ -138,7 +138,7 @@ setup: - do: indices.get_data_stream: - name: "*" + name: ['*', '-ilm-history*'] expand_wildcards: hidden - length: { data_streams: 2 } - match: { data_streams.0.name: .hidden-data-stream } diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml index 930b68b4361c8..8694a14448daa 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/80_resolve_index_data_streams.yml @@ -116,7 +116,7 @@ setup: - do: indices.resolve_index: - name: ['*','-.ml*', '-.ds-ilm-history*'] + name: ['*','-.ml*', '-.ds-ilm-history*', '-ilm-history*'] expand_wildcards: [all] - match: {indices.0.name: "/\\.ds-simple-data-stream1-(\\d{4}\\.\\d{2}\\.\\d{2}-)?000001/"} From de12ed27e023390a3ae712c2efc49ee74db88782 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 27 Sep 2023 10:24:31 +1000 Subject: [PATCH 087/155] Remove shard data files when they fail to write for snapshot (#99694) When shard level data files fail to write for snapshots, these data files become dangling and will not be referenced anywhere. Today we leave them as is. If the failure is disk full, the repository will become unusable because even the delete snapshot operation requires write a small metadata file first. This PR adds a clean up step so that the shard data files are removed if the shard data files fail to snapshot. Relates: #67790 Co-authored-by: David Turner --- docs/changelog/99694.yaml | 5 + .../blobstore/BlobStoreRepository.java | 21 ++- .../repositories/fs/FsRepositoryTests.java | 161 ++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 docs/changelog/99694.yaml diff --git a/docs/changelog/99694.yaml b/docs/changelog/99694.yaml new file mode 100644 index 0000000000000..a449ecb2ae378 --- /dev/null +++ b/docs/changelog/99694.yaml @@ -0,0 +1,5 @@ +pr: 99694 +summary: Remove shard data files when they fail to write for snapshot +area: Snapshot/Restore +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 971382fde57bb..7e3a2b531d366 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -55,6 +55,7 @@ import org.elasticsearch.common.blobstore.support.BlobMetadata; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.compress.NotXContentException; import org.elasticsearch.common.io.Streams; @@ -2943,8 +2944,9 @@ private void doSnapshotShard(SnapshotShardContext context) { }; } - final ListenableFuture> allFilesUploadedListener = new ListenableFuture<>(); - allFilesUploadedListener.addListener(context.delegateFailureAndWrap((delegate, v) -> { + // filesToSnapshot will be emptied while snapshotting the file. We make a copy here for cleanup purpose in case of failure. + final AtomicReference> fileToCleanUp = new AtomicReference<>(List.copyOf(filesToSnapshot)); + final ActionListener> allFilesUploadedListener = ActionListener.assertOnce(ActionListener.wrap(ignore -> { final IndexShardSnapshotStatus.Copy lastSnapshotStatus = snapshotStatus.moveToFinalize(); // now create and write the commit point @@ -2957,6 +2959,9 @@ private void doSnapshotShard(SnapshotShardContext context) { lastSnapshotStatus.getIncrementalFileCount(), lastSnapshotStatus.getIncrementalSize() ); + // Once we start writing the shard level snapshot file, no cleanup will be performed because it is possible that + // written files are referenced by another concurrent process. + fileToCleanUp.set(List.of()); try { final String snapshotUUID = snapshotId.getUUID(); final Map serializationParams = Collections.singletonMap( @@ -2980,8 +2985,18 @@ private void doSnapshotShard(SnapshotShardContext context) { getSegmentInfoFileCount(blobStoreIndexShardSnapshot.indexFiles()) ); snapshotStatus.moveToDone(threadPool.absoluteTimeInMillis(), shardSnapshotResult); - delegate.onResponse(shardSnapshotResult); + context.onResponse(shardSnapshotResult); + }, e -> { + try { + shardContainer.deleteBlobsIgnoringIfNotExists( + Iterators.flatMap(fileToCleanUp.get().iterator(), f -> Iterators.forRange(0, f.numberOfParts(), f::partName)) + ); + } catch (Exception innerException) { + e.addSuppressed(innerException); + } + context.onFailure(e); })); + if (indexIncrementalFileCount == 0 || filesToSnapshot.isEmpty()) { allFilesUploadedListener.onResponse(Collections.emptyList()); return; diff --git a/server/src/test/java/org/elasticsearch/repositories/fs/FsRepositoryTests.java b/server/src/test/java/org/elasticsearch/repositories/fs/FsRepositoryTests.java index 56ddaa2b825d5..66eaeb2da9108 100644 --- a/server/src/test/java/org/elasticsearch/repositories/fs/FsRepositoryTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/fs/FsRepositoryTests.java @@ -32,11 +32,17 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingHelper; import org.elasticsearch.cluster.routing.UnassignedInfo; +import org.elasticsearch.common.blobstore.BlobContainer; +import org.elasticsearch.common.blobstore.BlobPath; +import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.blobstore.support.FilterBlobContainer; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.concurrent.UncategorizedExecutionException; +import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; @@ -54,18 +60,28 @@ import org.elasticsearch.repositories.blobstore.BlobStoreTestUtil; import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotId; +import org.elasticsearch.snapshots.mockstore.BlobStoreWrapper; import org.elasticsearch.test.DummyShardLock; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.xcontent.NamedXContentRegistry; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; import static java.util.Collections.emptySet; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; public class FsRepositoryTests extends ESTestCase { @@ -192,6 +208,151 @@ public void testSnapshotAndRestore() throws IOException { } } + public void testCleanUpWhenShardDataFilesFailToWrite() throws IOException { + try (Directory directory = newDirectory()) { + Path repo = createTempDir(); + Settings settings = Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath()) + .put(Environment.PATH_REPO_SETTING.getKey(), repo.toAbsolutePath()) + .putList(Environment.PATH_DATA_SETTING.getKey(), tmpPaths()) + .put("location", repo) + .put("compress", randomBoolean()) + .put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES) + .build(); + + int numDocs = indexDocs(directory); + final var metadata = new RepositoryMetadata("test", "fs", settings); + final AtomicBoolean canErrorForWriteBlob = new AtomicBoolean(); + final AtomicBoolean shouldErrorForWriteMetadataBlob = new AtomicBoolean(); + final AtomicBoolean writeBlobErrored = new AtomicBoolean(false); + final var repository = new FsRepository( + metadata, + new Environment(settings, null), + NamedXContentRegistry.EMPTY, + BlobStoreTestUtil.mockClusterService(), + MockBigArrays.NON_RECYCLING_INSTANCE, + new RecoverySettings(settings, new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)) + ) { + @Override + protected BlobStore createBlobStore() throws Exception { + final BlobStore blobStore = super.createBlobStore(); + return new BlobStoreWrapper(blobStore) { + @Override + public BlobContainer blobContainer(BlobPath path) { + final BlobContainer blobContainer = blobStore.blobContainer(path); + return new FilterBlobContainer(blobContainer) { + @Override + public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) + throws IOException { + if (canErrorForWriteBlob.get() && randomIntBetween(0, 10) == 0) { + writeBlobErrored.set(true); + throw new IOException("disk full"); + } else { + super.writeBlob(blobName, inputStream, blobSize, failIfAlreadyExists); + } + } + + @Override + public void writeMetadataBlob( + String blobName, + boolean failIfAlreadyExists, + boolean atomic, + CheckedConsumer writer + ) throws IOException { + if (shouldErrorForWriteMetadataBlob.get() && blobName.startsWith("snap-")) { + throw new RuntimeException("snap file error"); + } + super.writeMetadataBlob(blobName, failIfAlreadyExists, atomic, writer); + } + + @Override + protected BlobContainer wrapChild(BlobContainer child) { + throw new UnsupportedOperationException(); + } + + }; + + } + }; + } + }; + repository.start(); + + final IndexSettings idxSettings = IndexSettingsModule.newIndexSettings( + "myindex", + Settings.builder().put(IndexMetadata.SETTING_INDEX_UUID, "myindexUUID").build() + ); + final ShardId shardId1 = new ShardId(idxSettings.getIndex(), 1); + final Store store1 = new Store(shardId1, idxSettings, directory, new DummyShardLock(shardId1)); + final SnapshotId snapshotId = new SnapshotId("test", "test"); + final IndexId indexId = new IndexId(idxSettings.getIndex().getName(), idxSettings.getUUID()); + IndexCommit indexCommit1 = Lucene.getIndexCommit(Lucene.readSegmentInfos(store1.directory()), store1.directory()); + final PlainActionFuture snapshot1Future = PlainActionFuture.newFuture(); + IndexShardSnapshotStatus snapshotStatus1 = IndexShardSnapshotStatus.newInitializing(null); + + // Scenario 1 - Shard data files will be cleaned up if they fail to write + canErrorForWriteBlob.set(true); + shouldErrorForWriteMetadataBlob.set(false); + repository.snapshotShard( + new SnapshotShardContext( + store1, + null, + snapshotId, + indexId, + new SnapshotIndexCommit(new Engine.IndexCommitRef(indexCommit1, () -> {})), + null, + snapshotStatus1, + IndexVersion.current(), + randomMillisUpToYear9999(), + snapshot1Future + ) + ); + if (writeBlobErrored.get()) { + final var e = expectThrows(UncategorizedExecutionException.class, snapshot1Future::actionGet); + assertThat(e.getCause().getCause(), instanceOf(IOException.class)); + assertThat(e.getCause().getCause().getMessage(), equalTo("disk full")); + + final Path shardSnapshotPath = repo.resolve("indices/myindexUUID/1"); + try (Stream pathStream = Files.list(shardSnapshotPath)) { + final List files = pathStream.filter(p -> p.getFileName().toString().startsWith("__")).toList(); + assertThat(files, empty()); + } + } else { + snapshot1Future.actionGet(); + } + + // Scenario 2 - Shard data files will not be cleaned up if shard level snap file fails to write + final ShardId shardId2 = new ShardId(idxSettings.getIndex(), 2); + final Store store2 = new Store(shardId2, idxSettings, directory, new DummyShardLock(shardId2)); + final IndexCommit indexCommit2 = Lucene.getIndexCommit(Lucene.readSegmentInfos(store2.directory()), store2.directory()); + final PlainActionFuture snapshot2Future = PlainActionFuture.newFuture(); + canErrorForWriteBlob.set(false); + shouldErrorForWriteMetadataBlob.set(true); + repository.snapshotShard( + new SnapshotShardContext( + store2, + null, + snapshotId, + indexId, + new SnapshotIndexCommit(new Engine.IndexCommitRef(indexCommit2, () -> {})), + null, + IndexShardSnapshotStatus.newInitializing(null), + IndexVersion.current(), + randomMillisUpToYear9999(), + snapshot2Future + ) + ); + final var e = expectThrows(RuntimeException.class, snapshot2Future::actionGet); + assertThat(e.getMessage(), equalTo("snap file error")); + + final Path shardSnapshotPath = repo.resolve("indices/myindexUUID/2"); + try (Stream pathStream = Files.list(shardSnapshotPath)) { + final List files = pathStream.filter(p -> p.getFileName().toString().startsWith("__")).toList(); + assertThat(files, not(empty())); + } + } + } + private void deleteRandomDoc(Directory directory) throws IOException { try ( IndexWriter writer = new IndexWriter( From b23e000c30b9f87373837182156a8fad8392346b Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Wed, 27 Sep 2023 08:48:21 +0200 Subject: [PATCH 088/155] Update gradle wrapper to 8.3 (#97838) Gradle now fully supports compiling, testing and running on Java 20. Among other general performance improvements this release introduces --test-dry-run command line option that allows checking if tests are filtered or not by gradle. Required updating nebula ospackage plugin as setuid was broken in gradle 8.3. --- .../gradle/wrapper/gradle-wrapper.properties | 4 +- ...acyYamlRestCompatTestPluginFuncTest.groovy | 5 +- .../ElasticsearchJavaModulePathPlugin.java | 38 +++-- .../internal/test/RestIntegTestTask.java | 2 +- .../AbstractYamlRestCompatTestPlugin.java | 1 + .../src/main/resources/minimumGradleVersion | 2 +- .../StandaloneRestIntegTestTask.java | 2 +- distribution/packages/build.gradle | 2 +- gradle/verification-metadata.xml | 161 +++++++++++++++++- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 3 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../watcher/qa/with-monitoring/build.gradle | 23 --- .../MonitoringWithWatcherRestIT.java | 111 ------------ 15 files changed, 194 insertions(+), 168 deletions(-) delete mode 100644 x-pack/plugin/watcher/qa/with-monitoring/build.gradle delete mode 100644 x-pack/plugin/watcher/qa/with-monitoring/src/javaRestTest/java/org/elasticsearch/smoketest/MonitoringWithWatcherRestIT.java diff --git a/build-tools-internal/gradle/wrapper/gradle-wrapper.properties b/build-tools-internal/gradle/wrapper/gradle-wrapper.properties index a3044fac5ab0b..6c7fa4d4653d2 100644 --- a/build-tools-internal/gradle/wrapper/gradle-wrapper.properties +++ b/build-tools-internal/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=5022b0b25fe182b0e50867e77f484501dba44feeea88f5c1f13b6b4660463640 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip +distributionSha256Sum=bb09982fdf52718e4c7b25023d10df6d35a5fff969860bdf5a5bd27a3ab27a9e +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy index 74f5b32dff3cc..644cf2183be16 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy @@ -55,7 +55,8 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe def result = gradleRunner("yamlRestTestV${compatibleVersion}CompatTest", '--stacktrace').build() then: - result.task(":yamlRestTestV${compatibleVersion}CompatTest").outcome == TaskOutcome.NO_SOURCE + // we set the task to be skipped if there are no matching tests in the compatibility test sourceSet + result.task(":yamlRestTestV${compatibleVersion}CompatTest").outcome == TaskOutcome.SKIPPED result.task(':copyRestCompatApiTask').outcome == TaskOutcome.NO_SOURCE result.task(':copyRestCompatTestTask').outcome == TaskOutcome.NO_SOURCE result.task(transformTask).outcome == TaskOutcome.NO_SOURCE @@ -164,7 +165,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe then: result.task(':check').outcome == TaskOutcome.UP_TO_DATE result.task(':checkRestCompat').outcome == TaskOutcome.UP_TO_DATE - result.task(":yamlRestTestV${compatibleVersion}CompatTest").outcome == TaskOutcome.NO_SOURCE + result.task(":yamlRestTestV${compatibleVersion}CompatTest").outcome == TaskOutcome.SKIPPED result.task(':copyRestCompatApiTask').outcome == TaskOutcome.NO_SOURCE result.task(':copyRestCompatTestTask').outcome == TaskOutcome.NO_SOURCE result.task(transformTask).outcome == TaskOutcome.NO_SOURCE diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java index 81865a577d985..662e1845850a7 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java @@ -43,7 +43,8 @@ /** * The Java Module Compile Path Plugin, i.e. --module-path, ---module-version */ -public class ElasticsearchJavaModulePathPlugin implements Plugin { +public abstract class ElasticsearchJavaModulePathPlugin implements Plugin { + @Override public void apply(Project project) { project.getPluginManager().apply(JavaPlugin.class); @@ -53,7 +54,7 @@ public void apply(Project project) { // List of root tasks, by name, whose compileJava task should not use the module path. These are test related sources. static final Set EXCLUDES = Set.of(":test:framework", ":x-pack:plugin:eql:qa:common"); - static void configureCompileModulePath(Project project) { + void configureCompileModulePath(Project project) { // first disable Gradle's builtin module path inference project.getTasks() .withType(JavaCompile.class) @@ -82,12 +83,10 @@ static void configureCompileModulePath(Project project) { }).getIncoming().artifactView(it -> { it.componentFilter(cf -> { var visited = new HashSet(); - return walkResolvedComponent( - project, - compileClasspath.getIncoming().getResolutionResult().getRoot(), - isModuleProject, - visited - ).anyMatch(cf::equals); + ResolvedComponentResult root = compileClasspath.getIncoming().getResolutionResult().getRoot(); + ComponentIdentifier id = root.getId(); + String currentBuildPath = ((ProjectComponentIdentifier) id).getBuild().getBuildPath(); + return walkResolvedComponent(currentBuildPath, project, root, isModuleProject, visited).anyMatch(cf::equals); }); }).getFiles(); @@ -116,7 +115,8 @@ public void execute(Task t) { }); } - static Stream walkResolvedComponent( + Stream walkResolvedComponent( + String currentBuildPath, Project project, ResolvedComponentResult result, boolean isModuleDependency, @@ -133,9 +133,10 @@ static Stream walkResolvedComponent( return false; } return isModuleDependency - || (it.getId() instanceof ProjectComponentIdentifier projectId && hasModuleInfoDotJava(project, projectId)); + || (it.getId() instanceof ProjectComponentIdentifier projectId + && hasModuleInfoDotJava(currentBuildPath, project, projectId)); }) - .flatMap(it -> Stream.concat(walkResolvedComponent(project, it, true, visited), Stream.of(it.getId()))); + .flatMap(it -> Stream.concat(walkResolvedComponent(currentBuildPath, project, it, true, visited), Stream.of(it.getId()))); } static class CompileModulePathArgumentProvider implements CommandLineArgumentProvider, Named { @@ -176,7 +177,7 @@ public String getName() { } } - static boolean hasModuleInfoDotJava(Project project) { + boolean hasModuleInfoDotJava(Project project) { return getJavaMainSourceSet(project).getJava() .getSrcDirs() .stream() @@ -184,8 +185,8 @@ static boolean hasModuleInfoDotJava(Project project) { .anyMatch(Files::exists); } - static boolean hasModuleInfoDotJava(Project project, ProjectComponentIdentifier id) { - return new File(findProjectIdPath(project, id), "src/main/java/module-info.java").exists(); + boolean hasModuleInfoDotJava(String currentBuildPath, Project project, ProjectComponentIdentifier id) { + return new File(findProjectIdPath(currentBuildPath, project, id), "src/main/java/module-info.java").exists(); } static SourceSet getJavaMainSourceSet(Project project) { @@ -209,13 +210,14 @@ static boolean isIdea() { return System.getProperty("idea.sync.active", "false").equals("true"); } - static File findProjectIdPath(Project project, ProjectComponentIdentifier id) { - if (id.getBuild().isCurrentBuild()) { + File findProjectIdPath(String currentBuildPath, Project project, ProjectComponentIdentifier id) { + if (id.getBuild().getBuildPath().equals(currentBuildPath)) { return project.findProject(id.getProjectPath()).getProjectDir(); } else { // For project dependencies sourced from an included build we have to infer the source project path - File includedBuildDir = project.getGradle().includedBuild(id.getBuild().getName()).getProjectDir(); - + String buildPath = id.getBuild().getBuildPath(); + String buildName = buildPath.substring(buildPath.lastIndexOf(':') + 1); + File includedBuildDir = project.getGradle().includedBuild(buildName).getProjectDir(); // We have to account for us renaming the :libs projects here String[] pathSegments = id.getProjectPath().split(":"); if (pathSegments[1].equals("libs")) { diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/RestIntegTestTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/RestIntegTestTask.java index f2a2434e60e73..8000536bc2525 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/RestIntegTestTask.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/RestIntegTestTask.java @@ -17,4 +17,4 @@ * conventional configured tasks of {@link RestIntegTestTask} */ @CacheableTask -public class RestIntegTestTask extends StandaloneRestIntegTestTask {} +public abstract class RestIntegTestTask extends StandaloneRestIntegTestTask {} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/compat/compat/AbstractYamlRestCompatTestPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/compat/compat/AbstractYamlRestCompatTestPlugin.java index 63433928feb5a..c6320394ef5b9 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/compat/compat/AbstractYamlRestCompatTestPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/compat/compat/AbstractYamlRestCompatTestPlugin.java @@ -221,6 +221,7 @@ public void apply(Project project) { testTask.setTestClassesDirs( yamlTestSourceSet.getOutput().getClassesDirs().plus(yamlCompatTestSourceSet.getOutput().getClassesDirs()) ); + testTask.onlyIf("Compatibility tests are available", t -> yamlCompatTestSourceSet.getAllSource().isEmpty() == false); testTask.setClasspath( yamlCompatTestSourceSet.getRuntimeClasspath() // remove the "normal" api and tests diff --git a/build-tools-internal/src/main/resources/minimumGradleVersion b/build-tools-internal/src/main/resources/minimumGradleVersion index 0dc0f32d56ef9..223a939307878 100644 --- a/build-tools-internal/src/main/resources/minimumGradleVersion +++ b/build-tools-internal/src/main/resources/minimumGradleVersion @@ -1 +1 @@ -8.2 \ No newline at end of file +8.3 \ No newline at end of file diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/StandaloneRestIntegTestTask.java b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/StandaloneRestIntegTestTask.java index b484bc04578d4..2bd8219dc48e5 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/StandaloneRestIntegTestTask.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/StandaloneRestIntegTestTask.java @@ -36,7 +36,7 @@ * {@link Nested} inputs. */ @CacheableTask -public class StandaloneRestIntegTestTask extends Test implements TestClustersAware, FileSystemOperationsAware { +public abstract class StandaloneRestIntegTestTask extends Test implements TestClustersAware, FileSystemOperationsAware { private Collection clusters = new HashSet<>(); private boolean debugServer = false; diff --git a/distribution/packages/build.gradle b/distribution/packages/build.gradle index c99a9af302c40..cecc5c7806240 100644 --- a/distribution/packages/build.gradle +++ b/distribution/packages/build.gradle @@ -43,7 +43,7 @@ import java.util.regex.Pattern */ plugins { - id "com.netflix.nebula.ospackage-base" version "11.0.0" + id "com.netflix.nebula.ospackage-base" version "11.5.0" } ['deb', 'rpm'].each { type -> diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index aec85d9ed5d6b..556da14241dcd 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -476,6 +476,11 @@ + + + + + @@ -516,6 +521,11 @@ + + + + + @@ -591,6 +601,11 @@ + + + + + @@ -771,9 +786,14 @@ - - - + + + + + + + + @@ -1361,6 +1381,16 @@ + + + + + + + + + + @@ -2669,11 +2699,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2897,6 +3002,11 @@ + + + + + @@ -2962,11 +3072,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3217,6 +3352,16 @@ + + + + + + + + + + @@ -3907,6 +4052,16 @@ + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 28216 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a3044fac5ab0b..6c7fa4d4653d2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=5022b0b25fe182b0e50867e77f484501dba44feeea88f5c1f13b6b4660463640 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip +distributionSha256Sum=bb09982fdf52718e4c7b25023d10df6d35a5fff969860bdf5a5bd27a3ab27a9e +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca147c0c..0adc8e1a53214 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/plugins/examples/gradle/wrapper/gradle-wrapper.properties b/plugins/examples/gradle/wrapper/gradle-wrapper.properties index a3044fac5ab0b..6c7fa4d4653d2 100644 --- a/plugins/examples/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=5022b0b25fe182b0e50867e77f484501dba44feeea88f5c1f13b6b4660463640 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip +distributionSha256Sum=bb09982fdf52718e4c7b25023d10df6d35a5fff969860bdf5a5bd27a3ab27a9e +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/x-pack/plugin/watcher/qa/with-monitoring/build.gradle b/x-pack/plugin/watcher/qa/with-monitoring/build.gradle deleted file mode 100644 index 6a2ef6fbd7bd5..0000000000000 --- a/x-pack/plugin/watcher/qa/with-monitoring/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -import org.elasticsearch.gradle.internal.info.BuildParams - -apply plugin: 'elasticsearch.legacy-java-rest-test' - -dependencies { - javaRestTestImplementation project(':x-pack:qa') - javaRestTestImplementation project(path: ':x-pack:plugin:watcher:qa:common') -} - -testClusters.configureEach { - testDistribution = 'DEFAULT' - setting 'xpack.monitoring.collection.enabled', 'true' - setting 'xpack.monitoring.collection.interval', '1s' - setting 'xpack.watcher.enabled', 'true' - setting 'xpack.security.enabled', 'false' - setting 'xpack.ml.enabled', 'false' - setting 'xpack.license.self_generated.type', 'trial' -} - -if (BuildParams.inFipsJvm){ - // Test clusters run with security disabled - tasks.named("javaRestTest").configure{enabled = false } -} diff --git a/x-pack/plugin/watcher/qa/with-monitoring/src/javaRestTest/java/org/elasticsearch/smoketest/MonitoringWithWatcherRestIT.java b/x-pack/plugin/watcher/qa/with-monitoring/src/javaRestTest/java/org/elasticsearch/smoketest/MonitoringWithWatcherRestIT.java deleted file mode 100644 index d8ccbfd7688fa..0000000000000 --- a/x-pack/plugin/watcher/qa/with-monitoring/src/javaRestTest/java/org/elasticsearch/smoketest/MonitoringWithWatcherRestIT.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.smoketest; - -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.common.Strings; -import org.elasticsearch.test.rest.ESRestTestCase; -import org.elasticsearch.test.rest.ObjectPath; -import org.junit.After; - -import java.io.IOException; - -import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.xpack.watcher.WatcherRestTestCase.deleteAllWatcherData; -import static org.hamcrest.Matchers.is; - -public class MonitoringWithWatcherRestIT extends ESRestTestCase { - - /** - * An unsorted list of Watch IDs representing resource files for Monitoring Cluster Alerts. - */ - public static final String[] WATCH_IDS = { - "elasticsearch_cluster_status", - "elasticsearch_version_mismatch", - "kibana_version_mismatch", - "logstash_version_mismatch", - "xpack_license_expiration", - "elasticsearch_nodes", }; - - @After - public void cleanExporters() throws Exception { - Request cleanupSettingsRequest = new Request("PUT", "/_cluster/settings"); - cleanupSettingsRequest.setJsonEntity( - Strings.toString( - jsonBuilder().startObject().startObject("persistent").nullField("xpack.monitoring.exporters.*").endObject().endObject() - ) - ); - adminClient().performRequest(cleanupSettingsRequest); - deleteAllWatcherData(); - } - - private void assertMonitoringWatchHasBeenOverWritten(String watchId) throws Exception { - assertBusy(() -> { - ObjectPath path = ObjectPath.createFromResponse(client().performRequest(new Request("GET", "/_watcher/watch/" + watchId))); - String interval = path.evaluate("watch.trigger.schedule.interval"); - assertThat(interval, is("1m")); - }); - } - - private void assertTotalWatchCount(int expectedWatches) throws Exception { - assertBusy(() -> { - refreshAllIndices(); - final Request countRequest = new Request("POST", "/_watcher/_query/watches"); - ObjectPath path = ObjectPath.createFromResponse(client().performRequest(countRequest)); - int count = path.evaluate("count"); - assertThat(count, is(expectedWatches)); - }); - } - - private String createMonitoringWatch() throws Exception { - String clusterUUID = getClusterUUID(); - String watchId = clusterUUID + "_kibana_version_mismatch"; - Request request = new Request("PUT", "/_watcher/watch/" + watchId); - String watch = """ - { - "trigger": { - "schedule": { - "interval": "1000m" - } - }, - "input": { - "simple": {} - }, - "condition": { - "always": {} - }, - "actions": { - "logme": { - "logging": { - "level": "info", - "text": "foo" - } - } - } - }"""; - request.setJsonEntity(watch); - client().performRequest(request); - return watchId; - } - - private String getClusterUUID() throws Exception { - Response response = client().performRequest(new Request("GET", "/_cluster/state/metadata")); - ObjectPath objectPath = ObjectPath.createFromResponse(response); - String clusterUUID = objectPath.evaluate("metadata.cluster_uuid"); - return clusterUUID; - } - - public String getHttpHost() throws IOException { - ObjectPath path = ObjectPath.createFromResponse(client().performRequest(new Request("GET", "/_cluster/state"))); - String masterNodeId = path.evaluate("master_node"); - - ObjectPath nodesPath = ObjectPath.createFromResponse(client().performRequest(new Request("GET", "/_nodes"))); - String httpHost = nodesPath.evaluate("nodes." + masterNodeId + ".http.publish_address"); - return httpHost; - } -} From a6b651a138ad50b534345fb4ab827db1e8112195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Wed, 27 Sep 2023 09:42:35 +0200 Subject: [PATCH 089/155] Replace Version.CURRENT toString usages with Build.current() equivalents (#99870) * Replaced some occurrences of Version.CURRENT toString * Added unit tests for the 2 different warn header patterns in HeaderWarnings * Comment to clarify non-semantic versions + revert change on Version.major message --- .../service/WindowsServiceInstallCommand.java | 6 +- .../WindowsServiceInstallCommandTests.java | 8 +- .../mustache/MultiSearchTemplateIT.java | 2 +- .../script/mustache/SearchTemplateIT.java | 3 +- .../elasticsearch/upgrades/RecoveryIT.java | 2 +- .../elasticsearch/env/NodeEnvironmentIT.java | 3 +- .../search/msearch/MultiSearchIT.java | 3 +- .../bootstrap/Elasticsearch.java | 4 +- .../DesiredNodesSettingsValidator.java | 8 +- .../metadata/IndexMetadataVerifier.java | 3 +- .../metadata/TemplateUpgradeService.java | 8 +- .../common/logging/HeaderWarning.java | 91 +++++++++++++------ .../elasticsearch/env/NodeEnvironment.java | 4 +- .../org/elasticsearch/env/NodeMetadata.java | 2 +- .../elasticsearch/rest/MethodHandlers.java | 2 +- .../transport/TransportService.java | 2 +- .../metadata/IndexMetadataVerifierTests.java | 6 +- .../common/logging/HeaderWarningTests.java | 19 ++++ .../elasticsearch/env/NodeMetadataTests.java | 6 +- .../org/elasticsearch/test/VersionUtils.java | 3 +- .../test/rest/ESRestTestCase.java | 4 +- .../TransportTermsEnumActionTests.java | 3 +- .../xpack/lucene/bwc/OldLuceneVersions.java | 6 +- .../sql/action/AbstractSqlQueryRequest.java | 7 +- .../xpack/sql/action/SqlQueryResponse.java | 6 +- 25 files changed, 140 insertions(+), 71 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java index 579c4cdf292b1..2df889be4a681 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java @@ -8,7 +8,7 @@ package org.elasticsearch.windows.service; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; @@ -47,7 +47,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg( args, "--DisplayName", - pinfo.envVars().getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Version.CURRENT, serviceId)) + pinfo.envVars().getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Build.current().version(), serviceId)) ); addArg( args, @@ -55,7 +55,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { pinfo.envVars() .getOrDefault( "SERVICE_DESCRIPTION", - String.format(java.util.Locale.ROOT, "Elasticsearch %s Windows Service - https://elastic.co", Version.CURRENT) + String.format(java.util.Locale.ROOT, "Elasticsearch %s Windows Service - https://elastic.co", Build.current().version()) ) ); addQuotedArg(args, "--Jvm", quote(getJvmDll(getJavaHome(pinfo.sysprops())).toString())); diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java index 7753e09bd9f46..5103d39c7311f 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java @@ -8,7 +8,7 @@ package org.elasticsearch.windows.service; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.core.Strings; @@ -154,13 +154,15 @@ public void testPidFile() throws Exception { } public void testDisplayName() throws Exception { - assertServiceArgs(Map.of("DisplayName", Strings.format("\"Elasticsearch %s (elasticsearch-service-x64)\"", Version.CURRENT))); + assertServiceArgs( + Map.of("DisplayName", Strings.format("\"Elasticsearch %s (elasticsearch-service-x64)\"", Build.current().version())) + ); envVars.put("SERVICE_DISPLAY_NAME", "my service name"); assertServiceArgs(Map.of("DisplayName", "\"my service name\"")); } public void testDescription() throws Exception { - String defaultDescription = Strings.format("\"Elasticsearch %s Windows Service - https://elastic.co\"", Version.CURRENT); + String defaultDescription = Strings.format("\"Elasticsearch %s Windows Service - https://elastic.co\"", Build.current().version()); assertServiceArgs(Map.of("Description", defaultDescription)); envVars.put("SERVICE_DESCRIPTION", "my description"); assertServiceArgs(Map.of("Description", "\"my description\"")); diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java index 45a0f60f4e1b8..3cd800fc82dec 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java @@ -182,7 +182,7 @@ public void testBasic() throws Exception { } /** - * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before Version.CURRENT works + * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before TransportVersion.current() works */ public void testCCSCheckCompatibility() throws Exception { String templateString = """ diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index 76605267e0e15..2ca332eabc029 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -351,7 +351,8 @@ public void testIndexedTemplateWithArray() throws Exception { } /** - * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before Version.CURRENT works + * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before + * TransportVersions.MINIMUM_CCS_VERSION works */ public void testCCSCheckCompatibility() throws Exception { String templateString = """ diff --git a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java index 077eae88fba02..f4ed3c2ea18c0 100644 --- a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java @@ -382,7 +382,7 @@ public void testRetentionLeasesEstablishedWhenRelocatingPrimary() throws Excepti final Map nodeDetailsMap = (Map) nodeDetails; final String versionString = (String) nodeDetailsMap.get("version"); if (versionString.equals(Version.CURRENT.toString()) == false) { - oldNodeNames.add((String) nodeDetailsMap.get("name")); + oldNodeNames.add(versionString); } } if (oldNodeNames.size() == 1) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java index 63c3297a4e5de..aced27c8c57eb 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java @@ -10,7 +10,6 @@ import org.elasticsearch.Build; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.CheckedConsumer; @@ -127,7 +126,7 @@ public void testFailsToStartIfDowngraded() { ); assertThat( illegalStateException.getMessage(), - allOf(startsWith("cannot downgrade a node from version ["), endsWith("] to version [" + Version.CURRENT + "]")) + allOf(startsWith("cannot downgrade a node from version ["), endsWith("] to version [" + Build.current().version() + "]")) ); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java index c8a12b7a90e30..6b3193c4b383c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java @@ -85,7 +85,8 @@ public void testSimpleMultiSearchMoreRequests() { } /** - * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before Version.CURRENT works + * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before + * TransportVersions.MINIMUM_CCS_VERSION works */ public void testCCSCheckCompatibility() throws Exception { TransportVersion transportVersion = TransportVersionUtils.getNextVersion(TransportVersions.MINIMUM_CCS_VERSION, true); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 0c165468dfba5..628d2bce28152 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -15,8 +15,8 @@ import org.apache.lucene.util.Constants; import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.VectorUtil; +import org.elasticsearch.Build; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.Version; import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.filesystem.FileSystemNatives; @@ -451,7 +451,7 @@ private Elasticsearch(Spawner spawner, Node node) { } catch (InterruptedException e) { // bail out } - }, "elasticsearch[keepAlive/" + Version.CURRENT + "]"); + }, "elasticsearch[keepAlive/" + Build.current().version() + "]"); } private void start() throws NodeValidationException { diff --git a/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java b/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java index 7c51af2bf7ffa..cd1298e4c6ef7 100644 --- a/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java +++ b/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java @@ -8,6 +8,7 @@ package org.elasticsearch.cluster.desirednodes; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.DesiredNode; import org.elasticsearch.common.settings.ClusterSettings; @@ -70,7 +71,12 @@ public void validate(List nodes) { private void validate(DesiredNode node) { if (node.version().before(Version.CURRENT)) { throw new IllegalArgumentException( - format(Locale.ROOT, "Illegal node version [%s]. Only [%s] or newer versions are supported", node.version(), Version.CURRENT) + format( + Locale.ROOT, + "Illegal node version [%s]. Only [%s] or newer versions are supported", + node.version(), + Build.current().version() + ) ); } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java index 63da2072983fb..e627b1088af0e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java @@ -12,6 +12,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.search.similarities.BM25Similarity; import org.apache.lucene.search.similarities.Similarity; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.TriFunction; @@ -116,7 +117,7 @@ private static void checkSupportedVersion(IndexMetadata indexMetadata, IndexVers + "]. It should be re-indexed in Elasticsearch " + (Version.CURRENT.major - 1) + ".x before upgrading to " - + Version.CURRENT + + Build.current().version() + "." ); } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java index 6dfabc695e1d4..f02e389157540 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java @@ -10,7 +10,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; @@ -118,7 +118,7 @@ public void clusterChanged(ClusterChangedEvent event) { if (upgradesInProgress.compareAndSet(0, changes.get().v1().size() + changes.get().v2().size() + 1)) { logger.info( "Starting template upgrade to version {}, {} templates will be updated and {} will be removed", - Version.CURRENT, + Build.current().version(), changes.get().v1().size(), changes.get().v2().size() ); @@ -190,9 +190,9 @@ void tryFinishUpgrade(AtomicBoolean anyUpgradeFailed) { try { // this is the last upgrade, the templates should now be in the desired state if (anyUpgradeFailed.get()) { - logger.info("Templates were partially upgraded to version {}", Version.CURRENT); + logger.info("Templates were partially upgraded to version {}", Build.current().version()); } else { - logger.info("Templates were upgraded successfully to version {}", Version.CURRENT); + logger.info("Templates were upgraded successfully to version {}", Build.current().version()); } // Check upgraders are satisfied after the update completed. If they still // report that changes are required, this might indicate a bug or that something diff --git a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java index 802aef18ebdc8..20ed4611106ac 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java +++ b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java @@ -9,7 +9,6 @@ package org.elasticsearch.common.logging; import org.elasticsearch.Build; -import org.elasticsearch.Version; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.tasks.Task; @@ -32,28 +31,63 @@ * The result will be returned as HTTP response headers. */ public class HeaderWarning { + + private static final String semanticVersionPattern = "\\d+\\.\\d+\\.\\d+(?:-(?:alpha|beta|rc)\\d+)?(?:-SNAPSHOT)?"; + + /** + * Detects if Build.current().version() returns a semantic version (major.minor.revision) or not + */ + private static final boolean hasSemanticVersion = Pattern.matches(semanticVersionPattern, Build.current().version()); + /** * Regular expression to test if a string matches the RFC7234 specification for warning headers. This pattern assumes that the warn code * is always 299. Further, this pattern assumes that the warn agent represents a version of Elasticsearch including the build - * hash. + * hash and (optionally) the build version. + * Build version is optional as Build.current().version() is extensible and may not be semantic in downstream projects or future + * releases. See {@link org.elasticsearch.internal.BuildExtension}. */ - public static final Pattern WARNING_HEADER_PATTERN = Pattern.compile("299 " + // log level code - "Elasticsearch-" + // warn agent - "\\d+\\.\\d+\\.\\d+(?:-(?:alpha|beta|rc)\\d+)?(?:-SNAPSHOT)?-" + // warn agent - "(?:[a-f0-9]{7}(?:[a-f0-9]{33})?|unknown) " + // warn agent - // quoted warning value, captured. Do not add more greedy qualifiers later to avoid excessive backtracking - "\"(?.*)\"( " + - // quoted RFC 1123 date format - "\"" + // opening quote - "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), " + // weekday - "\\d{2} " + // 2-digit day - "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + // month - "\\d{4} " + // 4-digit year - "\\d{2}:\\d{2}:\\d{2} " + // (two-digit hour):(two-digit minute):(two-digit second) - "GMT" + // GMT - "\")?",// closing quote (optional, since an older version can still send a warn-date) - Pattern.DOTALL - ); // in order to parse new line inside the qdText + public static final Pattern WARNING_HEADER_PATTERN = hasSemanticVersion + ? getPatternWithSemanticVersion() + : getPatternWithoutSemanticVersion(); + + static Pattern getPatternWithSemanticVersion() { + return Pattern.compile("299 " + // log level code + "Elasticsearch-" + // warn agent + semanticVersionPattern + "-" + // warn agent: semantic version + "(?:[a-f0-9]{7}(?:[a-f0-9]{33})?|unknown) " + // warn agent: hash + // quoted warning value, captured. Do not add more greedy qualifiers later to avoid excessive backtracking + "\"(?.*)\"( " + + // quoted RFC 1123 date format + "\"" + // opening quote + "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), " + // weekday + "\\d{2} " + // 2-digit day + "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + // month + "\\d{4} " + // 4-digit year + "\\d{2}:\\d{2}:\\d{2} " + // (two-digit hour):(two-digit minute):(two-digit second) + "GMT" + // GMT + "\")?",// closing quote (optional, since an older version can still send a warn-date) + Pattern.DOTALL + ); // in order to parse new line inside the qdText + } + + static Pattern getPatternWithoutSemanticVersion() { + return Pattern.compile("299 " + // log level code + "Elasticsearch-" + // warn agent + "(?:[a-f0-9]{7}(?:[a-f0-9]{33})?|unknown) " + // warn agent: hash + // quoted warning value, captured. Do not add more greedy qualifiers later to avoid excessive backtracking + "\"(?.*)\"( " + + // quoted RFC 1123 date format + "\"" + // opening quote + "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), " + // weekday + "\\d{2} " + // 2-digit day + "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + // month + "\\d{4} " + // 4-digit year + "\\d{2}:\\d{2}:\\d{2} " + // (two-digit hour):(two-digit minute):(two-digit second) + "GMT" + // GMT + "\")?",// closing quote (optional, since an older version can still send a warn-date) + Pattern.DOTALL + ); // in order to parse new line inside the qdText + } /** * quoted-string is defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 @@ -82,16 +116,17 @@ public class HeaderWarning { * RFC7234 specifies the warning format as warn-code warn-agent "warn-text" [ "warn-date"]. Here, warn-code is a * three-digit number with various standard warn codes specified. The warn code 299 is apt for our purposes as it represents a * miscellaneous persistent warning (can be presented to a human, or logged, and must not be removed by a cache). The warn-agent is an - * arbitrary token; here we use the Elasticsearch version and build hash. The warn text must be quoted. The warn-date is an optional - * quoted field that can be in a variety of specified date formats; here we use RFC 1123 format. + * arbitrary token; here we use the Elasticsearch version and build hash, if the version is semantic, or just the hash, if it is not + * semantic (e.g. in serverless). The warn text must be quoted. The warn-date is an optional quoted field that can be in a variety of + * specified date formats; here we use RFC 1123 format. */ - private static final String WARNING_PREFIX = String.format( - Locale.ROOT, - "299 Elasticsearch-%s%s-%s", - Version.CURRENT.toString(), - Build.current().isSnapshot() ? "-SNAPSHOT" : "", - Build.current().hash() - ); + private static final String WARNING_PREFIX = buildWarningPrefix(); + + private static String buildWarningPrefix() { + return hasSemanticVersion + ? String.format(Locale.ROOT, "299 Elasticsearch-%s-%s", Build.current().version(), Build.current().hash()) + : String.format(Locale.ROOT, "299 Elasticsearch-%s", Build.current().hash()); + } private static BitSet doesNotNeedEncoding; diff --git a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java index 1b40f3740b5b3..673484ac94c77 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -315,8 +315,8 @@ public NodeEnvironment(Settings settings, Environment environment) throws IOExce for (Path dataPath : environment.dataFiles()) { final Path legacyNodesPath = dataPath.resolve("nodes"); if (Files.isRegularFile(legacyNodesPath) == false) { - final String content = "written by Elasticsearch v" - + Version.CURRENT + final String content = "written by Elasticsearch " + + Build.current().version() + " to prevent a downgrade to a version prior to v8.0.0 which would result in data loss"; Files.writeString(legacyNodesPath, content); IOUtils.fsync(legacyNodesPath, false); diff --git a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java index fba5a0c96be3c..ecc29e793f331 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java +++ b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java @@ -128,7 +128,7 @@ public void verifyUpgradeToCurrentVersion() { if (nodeVersion.after(Version.CURRENT)) { throw new IllegalStateException( - "cannot downgrade a node from version [" + nodeVersion + "] to version [" + Version.CURRENT + "]" + "cannot downgrade a node from version [" + nodeVersion + "] to version [" + Build.current().version() + "]" ); } } diff --git a/server/src/main/java/org/elasticsearch/rest/MethodHandlers.java b/server/src/main/java/org/elasticsearch/rest/MethodHandlers.java index 1070b61108e48..804a2de8d0b64 100644 --- a/server/src/main/java/org/elasticsearch/rest/MethodHandlers.java +++ b/server/src/main/java/org/elasticsearch/rest/MethodHandlers.java @@ -55,7 +55,7 @@ MethodHandlers addMethod(RestRequest.Method method, RestApiVersion version, Rest /** * Returns the handler for the given method and version. * - * If a handler for given version do not exist, a handler for Version.CURRENT will be returned. + * If a handler for given version do not exist, a handler for RestApiVersion.current() will be returned. * The reasoning behind is that in a minor a new API could be added passively, therefore new APIs are compatible * (as opposed to non-compatible/breaking) * or {@code null} if none exists. diff --git a/server/src/main/java/org/elasticsearch/transport/TransportService.java b/server/src/main/java/org/elasticsearch/transport/TransportService.java index 77b417e0bbc84..8ef6ff3c9d8ef 100644 --- a/server/src/main/java/org/elasticsearch/transport/TransportService.java +++ b/server/src/main/java/org/elasticsearch/transport/TransportService.java @@ -673,7 +673,7 @@ private void throwOnIncompatibleBuild(@Nullable DiscoveryNode node, @Nullable Ex + "] but this node is build [" + Build.current().hash() + "] of version [" - + Version.CURRENT + + Build.current().version() + "] which has an incompatible wire format", e ); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java index f698d62ce03a1..9ae3afe1d9b2a 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java @@ -7,6 +7,7 @@ */ package org.elasticsearch.cluster.metadata; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.IndexScopedSettings; @@ -118,11 +119,10 @@ public void testIncompatibleVersion() { + "] " + "but the minimum compatible version is [" + minCompat - + "]." - + " It should be re-indexed in Elasticsearch " + + "]. It should be re-indexed in Elasticsearch " + (Version.CURRENT.major - 1) + ".x before upgrading to " - + Version.CURRENT + + Build.current().version() + "." ) ); diff --git a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java index c4793804e1913..bbb14fe6e9133 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java @@ -22,6 +22,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; import java.util.stream.IntStream; import static org.elasticsearch.common.logging.HeaderWarning.WARNING_HEADER_PATTERN; @@ -348,6 +349,24 @@ public void testHeaderWarningValidation() { HeaderWarning.addWarning(threadContexts, allNotAllowedChars()); } + public void testWarnAgentValidationStateful() { + var sampleWarningWithFullAgent = "299 Elasticsearch-8.11.0-SNAPSHOT-e4ccab7b7122041b8315194941bef592410916d0 " + + "\"[xpack.eql.enabled] setting was deprecated in Elasticsearch and will be removed in a future release.\""; + + var pattern = HeaderWarning.getPatternWithSemanticVersion(); + final Matcher matcher = pattern.matcher(sampleWarningWithFullAgent); + assertTrue("Warning on stateful/on-prem should match pattern with semantic version", matcher.matches()); + } + + public void testWarnAgentValidationStateless() { + var sampleWarningWithFullAgent = "299 Elasticsearch-e4ccab7b7122041b8315194941bef592410916d0 " + + "\"[xpack.eql.enabled] setting was deprecated in Elasticsearch and will be removed in a future release.\""; + + var pattern = HeaderWarning.getPatternWithoutSemanticVersion(); + final Matcher matcher = pattern.matcher(sampleWarningWithFullAgent); + assertTrue("Warning on stateless should match pattern without semantic version", matcher.matches()); + } + private String allNotAllowedChars() { StringBuilder chars = new StringBuilder(); for (char c = 0; c < 256; c++) { diff --git a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java index 309fcf78fe544..44ef31cfbd9d7 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java @@ -112,7 +112,9 @@ public void testUpgradesMissingVersion() { ); assertThat( illegalStateException.getMessage(), - startsWith("cannot upgrade a node from version [" + Version.V_EMPTY + "] directly to version [" + Version.CURRENT + "]") + startsWith( + "cannot upgrade a node from version [" + Version.V_EMPTY + "] directly to version [" + Build.current().version() + "]" + ) ); } @@ -123,7 +125,7 @@ public void testDoesNotUpgradeFutureVersion() { ); assertThat( illegalStateException.getMessage(), - allOf(startsWith("cannot downgrade a node from version ["), endsWith("] to version [" + Version.CURRENT + "]")) + allOf(startsWith("cannot downgrade a node from version ["), endsWith("] to version [" + Build.current().version() + "]")) ); } 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 56d470060150c..2f99c4ddaca36 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/VersionUtils.java @@ -8,6 +8,7 @@ package org.elasticsearch.test; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; @@ -177,7 +178,7 @@ public static Version getPreviousMinorVersion() { return v; } } - throw new IllegalArgumentException("couldn't find any released versions of the minor before [" + Version.CURRENT + "]"); + throw new IllegalArgumentException("couldn't find any released versions of the minor before [" + Build.current().version() + "]"); } /** Returns the oldest released {@link Version} */ diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index 21a78bfd38aad..9a3c03810c943 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -312,9 +312,9 @@ protected String getTestReadinessPorts() { public static class VersionSensitiveWarningsHandler implements WarningsHandler { Set requiredSameVersionClusterWarnings = new HashSet<>(); Set allowedWarnings = new HashSet<>(); - final Set testNodeVersions; + private final Set testNodeVersions; - public VersionSensitiveWarningsHandler(Set nodeVersions) { + VersionSensitiveWarningsHandler(Set nodeVersions) { this.testNodeVersions = nodeVersions; } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/termsenum/TransportTermsEnumActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/termsenum/TransportTermsEnumActionTests.java index bc38a1aa007b5..cb06dffead14f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/termsenum/TransportTermsEnumActionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/termsenum/TransportTermsEnumActionTests.java @@ -66,7 +66,8 @@ public void onFailure(final Exception e) { } /** - * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before Version.CURRENT works + * Test that triggering the CCS compatibility check with a query that shouldn't go to the minor before + * TransportVersions.MINIMUM_CCS_VERSION works */ public void testCCSCheckCompatibility() throws Exception { TermsEnumRequest request = new TermsEnumRequest().field("field").timeout(TimeValue.timeValueSeconds(5)); diff --git a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java index 8808482d2256f..5fc5d20461fe8 100644 --- a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java +++ b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java @@ -11,7 +11,7 @@ import org.apache.lucene.index.SegmentInfo; import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.client.internal.Client; @@ -197,9 +197,9 @@ private static void convertToNewFormat(IndexShard indexShard) { throw new UncheckedIOException( Strings.format( """ - Elasticsearch version [{}] has limited support for indices created in version [{}] but this index could not be \ + Elasticsearch version [{}] has limited support for indices created with version [{}] but this index could not be \ read. It may be using an unsupported feature, or it may be damaged or corrupt. See {} for further information.""", - Version.CURRENT, + Build.current().version(), IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexShard.indexSettings().getSettings()), ReferenceDocs.ARCHIVE_INDICES ), diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java index e4023a89d31ac..0f92b8905ef69 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java @@ -6,7 +6,9 @@ */ package org.elasticsearch.xpack.sql.action; +import org.elasticsearch.Build; import org.elasticsearch.TransportVersions; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.CompositeIndicesRequest; import org.elasticsearch.common.Strings; @@ -40,7 +42,6 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; -import static org.elasticsearch.Version.CURRENT; import static org.elasticsearch.action.ValidateActions.addValidationError; import static org.elasticsearch.common.xcontent.XContentParserUtils.parseFieldsValue; import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; @@ -287,7 +288,7 @@ public ActionRequestValidationException validate() { validationException ); } - } else if (SqlVersion.isClientCompatible(SqlVersion.fromId(CURRENT.id), requestInfo().version()) == false) { + } else if (SqlVersion.isClientCompatible(SqlVersion.fromId(Version.CURRENT.id), requestInfo().version()) == false) { validationException = addValidationError( "The [" + requestInfo().version() @@ -295,7 +296,7 @@ public ActionRequestValidationException validate() { + mode.toString() + "] " + "client is not compatible with Elasticsearch version [" - + CURRENT + + Build.current().version() + "]", validationException ); diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index 637b554847dfc..725eb0a2e3b01 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import org.elasticsearch.TransportVersions; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; @@ -29,7 +30,6 @@ import java.util.Objects; import static java.util.Collections.unmodifiableList; -import static org.elasticsearch.Version.CURRENT; import static org.elasticsearch.xpack.sql.action.AbstractSqlQueryRequest.CURSOR; import static org.elasticsearch.xpack.sql.proto.Mode.CLI; import static org.elasticsearch.xpack.sql.proto.Mode.JDBC; @@ -108,7 +108,7 @@ public SqlQueryResponse( ) { this.cursor = cursor; this.mode = mode; - this.sqlVersion = sqlVersion != null ? sqlVersion : fromId(CURRENT.id); + this.sqlVersion = sqlVersion != null ? sqlVersion : fromId(Version.CURRENT.id); this.columnar = columnar; this.columns = columns; this.rows = rows; @@ -276,7 +276,7 @@ private static XContentBuilder toXContent(ColumnInfo info, XContentBuilder build public static XContentBuilder value(XContentBuilder builder, Mode mode, SqlVersion sqlVersion, Object value) throws IOException { if (value instanceof ZonedDateTime zdt) { // use the ISO format - if (mode == JDBC && isClientCompatible(SqlVersion.fromId(CURRENT.id), sqlVersion)) { + if (mode == JDBC && isClientCompatible(SqlVersion.fromId(Version.CURRENT.id), sqlVersion)) { builder.value(StringUtils.toString(zdt, sqlVersion)); } else { builder.value(StringUtils.toString(zdt)); From d8a912429db27b5e2b2fee554e675a5d00ec45c7 Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Wed, 27 Sep 2023 10:38:22 +0200 Subject: [PATCH 090/155] [Transform] let _stats internally timeout if checkpoint information can not be retrieved (#99914) getting the stats of a transform can timeout, this change lets the retrieval of checkpointing info timeout if it takes too long. As stats are usually requested for all transforms, this prevents a failure if just 1 transform does not respond. --- docs/changelog/99914.yaml | 5 +++ .../TransportGetTransformStatsAction.java | 10 +++++- .../transform/transforms/TransformTask.java | 33 +++++++++++++------ 3 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 docs/changelog/99914.yaml diff --git a/docs/changelog/99914.yaml b/docs/changelog/99914.yaml new file mode 100644 index 0000000000000..8b0026a8ff9ca --- /dev/null +++ b/docs/changelog/99914.yaml @@ -0,0 +1,5 @@ +pr: 99914 +summary: Let `_stats` internally timeout if checkpoint information can not be retrieved +area: Transform +type: bug +issues: [] diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java index b988d2ab7296c..2649da6959205 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksCustomMetadata.Assignment; @@ -63,6 +64,9 @@ public class TransportGetTransformStatsAction extends TransportTasksAction listener + ActionListener listener, + TimeValue timeout ) { - ActionListener checkPointInfoListener = ActionListener.wrap(infoBuilder -> { - if (context.getChangesLastDetectedAt() != null) { - infoBuilder.setChangesLastDetectedAt(context.getChangesLastDetectedAt()); - } - if (context.getLastSearchTime() != null) { - infoBuilder.setLastSearchTime(context.getLastSearchTime()); - } - listener.onResponse(infoBuilder.build()); - }, listener::onFailure); + ActionListener checkPointInfoListener = ListenerTimeouts.wrapWithTimeout( + threadPool, + timeout, + threadPool.generic(), + ActionListener.wrap(infoBuilder -> { + if (context.getChangesLastDetectedAt() != null) { + infoBuilder.setChangesLastDetectedAt(context.getChangesLastDetectedAt()); + } + if (context.getLastSearchTime() != null) { + infoBuilder.setLastSearchTime(context.getLastSearchTime()); + } + listener.onResponse(infoBuilder.build()); + }, listener::onFailure), + (ignore) -> listener.onFailure( + new ElasticsearchTimeoutException(format("Timed out retrieving checkpointing info after [%s]", timeout)) + ) + ); + // TODO: pass `timeout` to the lower layers ClientTransformIndexer transformIndexer = getIndexer(); if (transformIndexer == null) { transformsCheckpointService.getCheckpointingInfo( From 191f65fa68303278f10c56e2e996522b25a00998 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 27 Sep 2023 19:08:21 +1000 Subject: [PATCH 091/155] [Test] Ensure correct initial datasetSize estimation (#99896) This PR accounts for the extra file created by ExtraFS for initial dataset size estimation. Resolves: #99762 --- .../src/test/java/org/elasticsearch/index/store/StoreTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/index/store/StoreTests.java b/server/src/test/java/org/elasticsearch/index/store/StoreTests.java index 2f1bdfd82c1c1..1c6314d60673d 100644 --- a/server/src/test/java/org/elasticsearch/index/store/StoreTests.java +++ b/server/src/test/java/org/elasticsearch/index/store/StoreTests.java @@ -815,7 +815,7 @@ public void testStoreSizes() throws IOException { // directory that returns total written bytes as the data set size final var directory = new ByteSizeDirectory(StoreTests.newDirectory(random())) { - final AtomicLong dataSetBytes = new AtomicLong(0L); + final AtomicLong dataSetBytes = new AtomicLong(estimateSizeInBytes(getDelegate())); @Override public long estimateSizeInBytes() throws IOException { From 9ea5250c0cb2b3c98aff56a6b9cea4af2422446b Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Wed, 27 Sep 2023 14:06:51 +0200 Subject: [PATCH 092/155] timeout can be null if upgraded from 7 (#99948) fix a regression introduced in #99914: the timeout variable can be null if upgraded from 7 relates #99914 fixes #99945 --- .../transform/action/TransportGetTransformStatsAction.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java index 2649da6959205..98343d5593dfa 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.client.internal.Client; @@ -138,7 +139,11 @@ protected void taskOperation(CancellableTask actionTask, Request request, Transf ), // at this point the transport already spend some time budget in `doExecute`, it is hard to tell what is left: // recording the time spend would be complex and crosses machine boundaries, that's why we use a heuristic here - TimeValue.timeValueMillis((long) (request.getTimeout().millis() * CHECKPOINT_INFO_TIMEOUT_SHARE)) + TimeValue.timeValueMillis( + (long) ((request.getTimeout() != null + ? request.getTimeout().millis() + : AcknowledgedRequest.DEFAULT_ACK_TIMEOUT.millis()) * CHECKPOINT_INFO_TIMEOUT_SHARE) + ) ); } else { listener.onResponse(new Response(Collections.emptyList())); From 096cf816706ab8c233076f5ebbb33c68aa87ef31 Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 27 Sep 2023 13:35:45 +0100 Subject: [PATCH 093/155] [ML] Make Inference Services pluggable (#99886) Creates an InferenceServicePlugins interface for inference services to implement and adds a test implementation to mock an inference service. --- server/src/main/java/module-info.java | 1 + .../inference}/InferenceResults.java | 17 +- .../inference}/InferenceService.java | 30 ++- .../inference/InferenceServiceRegistry.java | 69 +++++ .../org/elasticsearch}/inference/Model.java | 7 +- .../inference/ServiceSettings.java | 7 +- .../inference/TaskSettings.java | 7 +- .../elasticsearch}/inference/TaskType.java | 11 +- .../java/org/elasticsearch/node/Node.java | 12 +- .../plugins/InferenceServicePlugin.java | 43 ++++ .../core/ml/action/InferModelAction.java | 2 +- .../InferTrainedModelDeploymentAction.java | 2 +- .../MlInferenceNamedXContentProvider.java | 2 +- .../ClassificationInferenceResults.java | 1 + .../NlpClassificationInferenceResults.java | 4 +- .../results/NlpInferenceResults.java | 1 + .../QuestionAnsweringInferenceResults.java | 4 +- .../results/RawInferenceResults.java | 1 + .../results/SingleValueInferenceResults.java | 1 + .../results/WarningInferenceResults.java | 1 + .../inference/EnsembleInferenceModel.java | 2 +- .../inference/InferenceDefinition.java | 2 +- .../inference/InferenceModel.java | 2 +- .../inference/TreeInferenceModel.java | 2 +- .../langident/LangIdentNeuralNetwork.java | 2 +- .../action/InferModelActionResponseTests.java | 2 +- .../ClassificationInferenceResultsTests.java | 2 +- .../results/FillMaskResultsTests.java | 2 +- .../results/InferenceResultsTestCase.java | 1 + ...lpClassificationInferenceResultsTests.java | 2 +- ...uestionAnsweringInferenceResultsTests.java | 2 +- .../RegressionInferenceResultsTests.java | 2 +- .../results/TextExpansionResultsTests.java | 7 +- .../integration/MockInferenceServiceIT.java | 131 ++++++++++ .../integration/ModelRegistryIT.java | 8 +- .../TestInferenceServicePlugin.java | 238 ++++++++++++++++++ .../inference/src/main/java/module-info.java | 1 - .../InferenceNamedWriteablesProvider.java | 9 +- .../xpack/inference/InferencePlugin.java | 28 +-- .../xpack/inference/UnparsedModel.java | 2 + .../action/DeleteInferenceModelAction.java | 2 +- .../action/GetInferenceModelAction.java | 2 +- .../inference/action/InferenceAction.java | 14 +- .../action/PutInferenceModelAction.java | 4 +- .../TransportGetInferenceModelAction.java | 6 +- .../action/TransportInferenceAction.java | 16 +- .../TransportPutInferenceModelAction.java | 10 +- .../inference/registry/ModelRegistry.java | 2 +- .../inference/registry/ServiceRegistry.java | 31 --- .../inference/results/InferenceResult.java | 13 - .../results/SparseEmbeddingResult.java | 82 ------ .../inference/services/MapParsingUtils.java | 6 + .../services/elser/ElserMlNodeModel.java | 4 +- .../services/elser/ElserMlNodeService.java | 46 ++-- .../elser/ElserMlNodeServiceSettings.java | 4 +- .../elser/ElserMlNodeTaskSettings.java | 2 +- .../xpack/inference/ModelTests.java | 4 + .../action/GetInferenceModelRequestTests.java | 2 +- .../action/InferenceActionRequestTests.java | 2 +- .../action/InferenceActionResponseTests.java | 12 +- .../action/PutInferenceModelRequestTests.java | 2 +- .../registry/ServiceRegistryTests.java | 28 --- .../results/SparseEmbeddingResultTests.java | 47 ---- .../elser/ElserMlNodeServiceTests.java | 14 +- .../integration/ModelInferenceActionIT.java | 2 +- ...portInferTrainedModelDeploymentAction.java | 2 +- .../TransportInternalInferModelAction.java | 2 +- .../InferencePipelineAggregator.java | 2 +- .../InternalInferenceAggregation.java | 4 +- .../dataframe/inference/InferenceRunner.java | 2 +- .../TrainedModelAssignmentNodeService.java | 2 +- .../deployment/DeploymentManager.java | 2 +- .../deployment/InferencePyTorchAction.java | 2 +- .../TrainedModelDeploymentTask.java | 2 +- .../inference/ingest/InferenceProcessor.java | 4 +- .../inference/loadingservice/LocalModel.java | 2 +- .../loadingservice/ModelLoadingService.java | 2 +- .../ml/inference/nlp/FillMaskProcessor.java | 2 +- .../xpack/ml/inference/nlp/NerProcessor.java | 2 +- .../xpack/ml/inference/nlp/NlpTask.java | 2 +- .../inference/nlp/PassThroughProcessor.java | 2 +- .../nlp/QuestionAnsweringProcessor.java | 2 +- .../nlp/TextClassificationProcessor.java | 2 +- .../inference/nlp/TextEmbeddingProcessor.java | 2 +- .../inference/nlp/TextExpansionProcessor.java | 2 +- .../nlp/TextSimilarityProcessor.java | 2 +- .../nlp/ZeroShotClassificationProcessor.java | 2 +- .../inference/rescorer/InferenceRescorer.java | 2 +- .../InferenceProcessorInfoExtractor.java | 2 +- .../InternalInferenceAggregationTests.java | 2 +- .../ml/aggs/inference/ParsedInference.java | 2 +- .../inference/InferenceRunnerTests.java | 2 +- .../InferencePyTorchActionTests.java | 2 +- .../InferenceProcessorFactoryTests.java | 2 +- .../loadingservice/LocalModelTests.java | 4 +- .../ModelLoadingServiceTests.java | 2 +- .../InferenceProcessorInfoExtractorTests.java | 2 +- 97 files changed, 705 insertions(+), 392 deletions(-) rename {x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results => server/src/main/java/org/elasticsearch/inference}/InferenceResults.java (68%) rename {x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services => server/src/main/java/org/elasticsearch/inference}/InferenceService.java (74%) create mode 100644 server/src/main/java/org/elasticsearch/inference/InferenceServiceRegistry.java rename {x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack => server/src/main/java/org/elasticsearch}/inference/Model.java (94%) rename {x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack => server/src/main/java/org/elasticsearch}/inference/ServiceSettings.java (62%) rename {x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack => server/src/main/java/org/elasticsearch}/inference/TaskSettings.java (62%) rename {x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack => server/src/main/java/org/elasticsearch}/inference/TaskType.java (77%) create mode 100644 server/src/main/java/org/elasticsearch/plugins/InferenceServicePlugin.java create mode 100644 x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/MockInferenceServiceIT.java create mode 100644 x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/TestInferenceServicePlugin.java delete mode 100644 x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ServiceRegistry.java delete mode 100644 x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/InferenceResult.java delete mode 100644 x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResult.java delete mode 100644 x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/registry/ServiceRegistryTests.java delete mode 100644 x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResultTests.java diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 1568f47461e6c..99ce5910c9775 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -278,6 +278,7 @@ exports org.elasticsearch.indices.recovery; exports org.elasticsearch.indices.recovery.plan; exports org.elasticsearch.indices.store; + exports org.elasticsearch.inference; exports org.elasticsearch.ingest; exports org.elasticsearch.internal to diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResults.java b/server/src/main/java/org/elasticsearch/inference/InferenceResults.java similarity index 68% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResults.java rename to server/src/main/java/org/elasticsearch/inference/InferenceResults.java index 83f08391c656b..76dfcae2ba530 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResults.java +++ b/server/src/main/java/org/elasticsearch/inference/InferenceResults.java @@ -1,26 +1,27 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.core.ml.inference.results; + +package org.elasticsearch.inference; import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.xcontent.ToXContentFragment; -import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import java.util.Map; +import java.util.Objects; public interface InferenceResults extends NamedWriteable, ToXContentFragment { - String PREDICTION_PROBABILITY = "prediction_probability"; String MODEL_ID_RESULTS_FIELD = "model_id"; static void writeResult(InferenceResults results, IngestDocument ingestDocument, String resultField, String modelId) { - ExceptionsHelper.requireNonNull(results, "results"); - ExceptionsHelper.requireNonNull(ingestDocument, "ingestDocument"); - ExceptionsHelper.requireNonNull(resultField, "resultField"); + Objects.requireNonNull(results, "results"); + Objects.requireNonNull(ingestDocument, "ingestDocument"); + Objects.requireNonNull(resultField, "resultField"); Map resultMap = results.asMap(); resultMap.put(MODEL_ID_RESULTS_FIELD, modelId); if (ingestDocument.hasField(resultField)) { diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/InferenceService.java b/server/src/main/java/org/elasticsearch/inference/InferenceService.java similarity index 74% rename from x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/InferenceService.java rename to server/src/main/java/org/elasticsearch/inference/InferenceService.java index 18704f4c32740..3bc9cf2a5a116 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/InferenceService.java +++ b/server/src/main/java/org/elasticsearch/inference/InferenceService.java @@ -1,16 +1,14 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.inference.services; +package org.elasticsearch.inference; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.TaskType; -import org.elasticsearch.xpack.inference.results.InferenceResult; import java.util.Map; @@ -45,20 +43,20 @@ public interface InferenceService { */ Model parseConfigLenient(String modelId, TaskType taskType, Map config); - /** - * Start or prepare the model for use. - * @param model The model - * @param listener The listener - */ - void start(Model model, ActionListener listener); - /** * Perform inference on the model. * - * @param model Model configuration + * @param model The model * @param input Inference input - * @param requestTaskSettings Settings in the request to override the model's defaults + * @param taskSettings Settings in the request to override the model's defaults * @param listener Inference result listener */ - void infer(Model model, String input, Map requestTaskSettings, ActionListener listener); + void infer(Model model, String input, Map taskSettings, ActionListener listener); + + /** + * Start or prepare the model for use. + * @param model The model + * @param listener The listener + */ + void start(Model model, ActionListener listener); } diff --git a/server/src/main/java/org/elasticsearch/inference/InferenceServiceRegistry.java b/server/src/main/java/org/elasticsearch/inference/InferenceServiceRegistry.java new file mode 100644 index 0000000000000..ac1439150f8ec --- /dev/null +++ b/server/src/main/java/org/elasticsearch/inference/InferenceServiceRegistry.java @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.inference; + +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.plugins.InferenceServicePlugin; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class InferenceServiceRegistry extends AbstractLifecycleComponent { + + private final Map services; + private final List namedWriteables = new ArrayList<>(); + + public InferenceServiceRegistry( + List inferenceServicePlugins, + InferenceServicePlugin.InferenceServiceFactoryContext factoryContext + ) { + // TODO check names are unique + services = inferenceServicePlugins.stream() + .flatMap(r -> r.getInferenceServiceFactories().stream()) + .map(factory -> factory.create(factoryContext)) + .collect(Collectors.toMap(InferenceService::name, Function.identity())); + + for (var plugin : inferenceServicePlugins) { + namedWriteables.addAll(plugin.getInferenceServiceNamedWriteables()); + } + } + + public Map getServices() { + return services; + } + + public Optional getService(String serviceName) { + return Optional.ofNullable(services.get(serviceName)); + } + + public List getNamedWriteables() { + return namedWriteables; + } + + @Override + protected void doStart() { + + } + + @Override + protected void doStop() { + + } + + @Override + protected void doClose() throws IOException { + + } +} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/Model.java b/server/src/main/java/org/elasticsearch/inference/Model.java similarity index 94% rename from x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/Model.java rename to server/src/main/java/org/elasticsearch/inference/Model.java index c0032ebf25ca7..67ee58bad733c 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/Model.java +++ b/server/src/main/java/org/elasticsearch/inference/Model.java @@ -1,11 +1,12 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.inference; +package org.elasticsearch.inference; import org.elasticsearch.TransportVersion; import org.elasticsearch.TransportVersions; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/ServiceSettings.java b/server/src/main/java/org/elasticsearch/inference/ServiceSettings.java similarity index 62% rename from x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/ServiceSettings.java rename to server/src/main/java/org/elasticsearch/inference/ServiceSettings.java index 16f39ad8e560c..01a75158f5ca2 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/ServiceSettings.java +++ b/server/src/main/java/org/elasticsearch/inference/ServiceSettings.java @@ -1,11 +1,12 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.inference; +package org.elasticsearch.inference; import org.elasticsearch.common.io.stream.VersionedNamedWriteable; import org.elasticsearch.xcontent.ToXContentObject; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskSettings.java b/server/src/main/java/org/elasticsearch/inference/TaskSettings.java similarity index 62% rename from x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskSettings.java rename to server/src/main/java/org/elasticsearch/inference/TaskSettings.java index 200f1b309822d..e346fa89b8663 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskSettings.java +++ b/server/src/main/java/org/elasticsearch/inference/TaskSettings.java @@ -1,11 +1,12 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.inference; +package org.elasticsearch.inference; import org.elasticsearch.common.io.stream.VersionedNamedWriteable; import org.elasticsearch.xcontent.ToXContentObject; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskType.java b/server/src/main/java/org/elasticsearch/inference/TaskType.java similarity index 77% rename from x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskType.java rename to server/src/main/java/org/elasticsearch/inference/TaskType.java index 5e9cc93270033..9e96a7c4c52d0 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/TaskType.java +++ b/server/src/main/java/org/elasticsearch/inference/TaskType.java @@ -1,11 +1,12 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -package org.elasticsearch.xpack.inference; +package org.elasticsearch.inference; import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.common.io.stream.StreamInput; @@ -49,4 +50,8 @@ public static TaskType fromStream(StreamInput in) throws IOException { public void writeTo(StreamOutput out) throws IOException { out.writeEnum(this); } + + public static String unsupportedTaskTypeErrorMsg(TaskType taskType, String serviceName) { + return "The [" + serviceName + "] service does not support task type [" + taskType + "]"; + } } diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index ad8ad68bf7650..a2da65bdaa8b4 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -146,6 +146,7 @@ import org.elasticsearch.indices.recovery.plan.RecoveryPlannerService; import org.elasticsearch.indices.recovery.plan.ShardSnapshotsService; import org.elasticsearch.indices.store.IndicesStore; +import org.elasticsearch.inference.InferenceServiceRegistry; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.monitor.MonitorService; import org.elasticsearch.monitor.fs.FsHealthService; @@ -165,6 +166,7 @@ import org.elasticsearch.plugins.EnginePlugin; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.IndexStorePlugin; +import org.elasticsearch.plugins.InferenceServicePlugin; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.MetadataUpgrader; @@ -532,6 +534,12 @@ protected Node( Supplier documentParsingObserverSupplier = getDocumentParsingObserverSupplier(); + var factoryContext = new InferenceServicePlugin.InferenceServiceFactoryContext(client); + final InferenceServiceRegistry inferenceServiceRegistry = new InferenceServiceRegistry( + pluginsService.filterPlugins(InferenceServicePlugin.class), + factoryContext + ); + final IngestService ingestService = new IngestService( clusterService, threadPool, @@ -555,7 +563,8 @@ protected Node( searchModule.getNamedWriteables().stream(), pluginsService.flatMap(Plugin::getNamedWriteables), ClusterModule.getNamedWriteables().stream(), - SystemIndexMigrationExecutor.getNamedWriteables().stream() + SystemIndexMigrationExecutor.getNamedWriteables().stream(), + inferenceServiceRegistry.getNamedWriteables().stream() ).flatMap(Function.identity()).toList(); final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(namedWriteables); NamedXContentRegistry xContentRegistry = new NamedXContentRegistry( @@ -1170,6 +1179,7 @@ protected Node( b.bind(WriteLoadForecaster.class).toInstance(writeLoadForecaster); b.bind(HealthPeriodicLogger.class).toInstance(healthPeriodicLogger); b.bind(CompatibilityVersions.class).toInstance(compatibilityVersions); + b.bind(InferenceServiceRegistry.class).toInstance(inferenceServiceRegistry); }); if (ReadinessService.enabled(environment)) { diff --git a/server/src/main/java/org/elasticsearch/plugins/InferenceServicePlugin.java b/server/src/main/java/org/elasticsearch/plugins/InferenceServicePlugin.java new file mode 100644 index 0000000000000..2672a4b8fcbcf --- /dev/null +++ b/server/src/main/java/org/elasticsearch/plugins/InferenceServicePlugin.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.plugins; + +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.inference.InferenceService; + +import java.util.List; + +/** + * InferenceServicePlugins implement an inference service + */ +public interface InferenceServicePlugin { + + List getInferenceServiceFactories(); + + record InferenceServiceFactoryContext(Client client) {} + + interface Factory { + /** + * InferenceServices are created from the factory context + */ + InferenceService create(InferenceServiceFactoryContext context); + } + + /** + * The named writables defined and used by each of the implemented + * InferenceServices. Each service should define named writables for + * - {@link org.elasticsearch.inference.TaskSettings} + * - {@link org.elasticsearch.inference.ServiceSettings} + * And optionally for {@link org.elasticsearch.inference.InferenceResults} + * if the service uses a new type of result. + * @return All named writables defined by the services + */ + List getInferenceServiceNamedWriteables(); +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferModelAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferModelAction.java index fdc4040b0be49..a06d0fe0ce0ce 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferModelAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferModelAction.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -22,7 +23,6 @@ import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.EmptyConfigUpdate; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfigUpdate; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferTrainedModelDeploymentAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferTrainedModelDeploymentAction.java index 916f09c84b6f5..524d5f84a177b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferTrainedModelDeploymentAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/InferTrainedModelDeploymentAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; @@ -25,7 +26,6 @@ import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.EmptyConfigUpdate; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfigUpdate; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/MlInferenceNamedXContentProvider.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/MlInferenceNamedXContentProvider.java index 1ef60af3584e7..7f0d12af5f465 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/MlInferenceNamedXContentProvider.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/MlInferenceNamedXContentProvider.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.ml.inference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.plugins.spi.NamedXContentProvider; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; @@ -21,7 +22,6 @@ import org.elasticsearch.xpack.core.ml.inference.preprocessing.TargetMeanEncoding; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.FillMaskResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.NerResults; import org.elasticsearch.xpack.core.ml.inference.results.NlpClassificationInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.PyTorchPassThroughResults; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResults.java index 475e39c8eb3d2..ebf946aa34716 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResults.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; public class ClassificationInferenceResults extends SingleValueInferenceResults { + public static String PREDICTION_PROBABILITY = "prediction_probability"; public static final String NAME = "classification"; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResults.java index 7556b223dd317..a49e81e40a7a6 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResults.java @@ -107,7 +107,7 @@ void addMapFields(Map map) { ); } if (predictionProbability != null) { - map.put(PREDICTION_PROBABILITY, predictionProbability); + map.put(ClassificationInferenceResults.PREDICTION_PROBABILITY, predictionProbability); } } @@ -123,7 +123,7 @@ public void doXContentBody(XContentBuilder builder, Params params) throws IOExce builder.field(NlpConfig.DEFAULT_TOP_CLASSES_RESULTS_FIELD, topClasses); } if (predictionProbability != null) { - builder.field(PREDICTION_PROBABILITY, predictionProbability); + builder.field(ClassificationInferenceResults.PREDICTION_PROBABILITY, predictionProbability); } } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpInferenceResults.java index 33ef519f5e41c..ab1939013e976 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/NlpInferenceResults.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResults.java index 0c60b05320cec..dec5343fe8ddf 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResults.java @@ -129,7 +129,7 @@ void addMapFields(Map map) { topClasses.stream().map(TopAnswerEntry::asValueMap).collect(Collectors.toList()) ); } - map.put(PREDICTION_PROBABILITY, score); + map.put(ClassificationInferenceResults.PREDICTION_PROBABILITY, score); } @Override @@ -145,7 +145,7 @@ public void doXContentBody(XContentBuilder builder, Params params) throws IOExce if (topClasses.size() > 0) { builder.field(NlpConfig.DEFAULT_TOP_CLASSES_RESULTS_FIELD, topClasses); } - builder.field(PREDICTION_PROBABILITY, score); + builder.field(ClassificationInferenceResults.PREDICTION_PROBABILITY, score); } public int getStartOffset() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/RawInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/RawInferenceResults.java index 97b2949d0020c..b7744a6c54800 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/RawInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/RawInferenceResults.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.ml.inference.results; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/SingleValueInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/SingleValueInferenceResults.java index 0daaa1ebc35a9..1da92021a9edb 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/SingleValueInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/SingleValueInferenceResults.java @@ -8,6 +8,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; import java.io.IOException; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/WarningInferenceResults.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/WarningInferenceResults.java index fc399b8298335..a956c91007934 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/WarningInferenceResults.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/results/WarningInferenceResults.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.XContentBuilder; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/EnsembleInferenceModel.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/EnsembleInferenceModel.java index 30d1eda66a39c..10f0cc6770a9c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/EnsembleInferenceModel.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/EnsembleInferenceModel.java @@ -13,10 +13,10 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RawInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RegressionInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceDefinition.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceDefinition.java index 7ae5b141a3c0f..636cbcac725f4 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceDefinition.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceDefinition.java @@ -8,11 +8,11 @@ package org.elasticsearch.xpack.core.ml.inference.trainedmodel.inference; import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.ml.inference.preprocessing.LenientlyParsedPreProcessor; import org.elasticsearch.xpack.core.ml.inference.preprocessing.PreProcessor; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TargetType; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceModel.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceModel.java index 61498648c8c01..cf532cefbeab8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceModel.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/InferenceModel.java @@ -9,7 +9,7 @@ import org.apache.lucene.util.Accountable; import org.elasticsearch.core.Nullable; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceHelpers; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TargetType; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/TreeInferenceModel.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/TreeInferenceModel.java index b1d166a00a461..d23600383b34b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/TreeInferenceModel.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/inference/TreeInferenceModel.java @@ -13,11 +13,11 @@ import org.elasticsearch.common.Numbers; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RawInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RegressionInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/langident/LangIdentNeuralNetwork.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/langident/LangIdentNeuralNetwork.java index 8402e0eaade5e..8d010c6fbc650 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/langident/LangIdentNeuralNetwork.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/trainedmodel/langident/LangIdentNeuralNetwork.java @@ -11,13 +11,13 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.ml.inference.preprocessing.CustomWordEmbedding; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/InferModelActionResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/InferModelActionResponseTests.java index 591e085ff6f36..4d8035864729a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/InferModelActionResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/InferModelActionResponseTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.core.ml.action.InferModelAction.Response; import org.elasticsearch.xpack.core.ml.inference.MlInferenceNamedXContentProvider; @@ -16,7 +17,6 @@ import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResultsTests; import org.elasticsearch.xpack.core.ml.inference.results.FillMaskResults; import org.elasticsearch.xpack.core.ml.inference.results.FillMaskResultsTests; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.NerResults; import org.elasticsearch.xpack.core.ml.inference.results.NerResultsTests; import org.elasticsearch.xpack.core.ml.inference.results.PyTorchPassThroughResults; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResultsTests.java index 492678f035562..28e318c0dab48 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/ClassificationInferenceResultsTests.java @@ -23,7 +23,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.writeResult; +import static org.elasticsearch.inference.InferenceResults.writeResult; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/FillMaskResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/FillMaskResultsTests.java index 4119d56be7b5b..432e05b9cc680 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/FillMaskResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/FillMaskResultsTests.java @@ -14,7 +14,7 @@ import java.util.List; import java.util.Map; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.PREDICTION_PROBABILITY; +import static org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults.PREDICTION_PROBABILITY; import static org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig.DEFAULT_RESULTS_FIELD; import static org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig.DEFAULT_TOP_CLASSES_RESULTS_FIELD; import static org.hamcrest.Matchers.equalTo; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResultsTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResultsTestCase.java index 0b5d6a0b9c92b..27503547e5705 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResultsTestCase.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/InferenceResultsTestCase.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.core.ml.inference.results; import org.elasticsearch.TransportVersion; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.TestIngestDocument; import org.elasticsearch.test.AbstractWireSerializingTestCase; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResultsTests.java index d47730bd579a6..ac3cb638d88d1 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/NlpClassificationInferenceResultsTests.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.writeResult; +import static org.elasticsearch.inference.InferenceResults.writeResult; import static org.hamcrest.Matchers.equalTo; public class NlpClassificationInferenceResultsTests extends InferenceResultsTestCase { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResultsTests.java index 947f8732aba68..c9c65ea3f3538 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/QuestionAnsweringInferenceResultsTests.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.writeResult; +import static org.elasticsearch.inference.InferenceResults.writeResult; import static org.hamcrest.Matchers.equalTo; public class QuestionAnsweringInferenceResultsTests extends InferenceResultsTestCase { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/RegressionInferenceResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/RegressionInferenceResultsTests.java index 6ea00fce676e4..27a07e8f996f7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/RegressionInferenceResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/RegressionInferenceResultsTests.java @@ -19,7 +19,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.writeResult; +import static org.elasticsearch.inference.InferenceResults.writeResult; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/TextExpansionResultsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/TextExpansionResultsTests.java index 104838921a434..c3b2fbf6fb556 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/TextExpansionResultsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/results/TextExpansionResultsTests.java @@ -16,8 +16,13 @@ import java.util.stream.Collectors; public class TextExpansionResultsTests extends InferenceResultsTestCase { + public static TextExpansionResults createRandomResults() { - int numTokens = randomIntBetween(0, 20); + return createRandomResults(0, 20); + } + + public static TextExpansionResults createRandomResults(int min, int max) { + int numTokens = randomIntBetween(min, max); List tokenList = new ArrayList<>(); for (int i = 0; i < numTokens; i++) { tokenList.add(new TextExpansionResults.WeightedToken(Integer.toString(i), (float) randomDoubleBetween(0.0, 5.0, false))); diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/MockInferenceServiceIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/MockInferenceServiceIT.java new file mode 100644 index 0000000000000..411c29255fd78 --- /dev/null +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/MockInferenceServiceIT.java @@ -0,0 +1,131 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.inference.integration; + +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.SecuritySettingsSourceField; +import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; +import org.elasticsearch.xpack.inference.InferencePlugin; +import org.elasticsearch.xpack.inference.action.GetInferenceModelAction; +import org.elasticsearch.xpack.inference.action.InferenceAction; +import org.elasticsearch.xpack.inference.action.PutInferenceModelAction; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; + +public class MockInferenceServiceIT extends ESIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return List.of(InferencePlugin.class, TestInferenceServicePlugin.class); + } + + @Override + protected Function getClientWrapper() { + final Map headers = Map.of( + "Authorization", + basicAuthHeaderValue("x_pack_rest_user", SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING) + ); + // we need to wrap node clients because we do not specify a user for nodes and all requests will use the system + // user. This is ok for internal n2n stuff but the test framework does other things like wiping indices, repositories, etc + // that the system user cannot do. so we wrap the node client with a user that can do these things since the client() calls + // return a node client + return client -> client.filterWithHeader(headers); + } + + public void testMockService() { + String modelId = "test-mock"; + Model putModel = putMockService(modelId, TaskType.SPARSE_EMBEDDING); + Model readModel = getModel(modelId, TaskType.SPARSE_EMBEDDING); + assertModelsAreEqual(putModel, readModel); + + // The response is randomly generated, the input can be anything + inferOnMockService(modelId, TaskType.SPARSE_EMBEDDING, randomAlphaOfLength(10)); + } + + private Model putMockService(String modelId, TaskType taskType) { + String body = """ + { + "service": "test_service", + "service_settings": { + "model": "my_model", + "api_key": "abc64" + }, + "task_settings": { + "temperature": 3 + } + } + """; + var request = new PutInferenceModelAction.Request( + taskType.toString(), + modelId, + new BytesArray(body.getBytes(StandardCharsets.UTF_8)), + XContentType.JSON + ); + + var response = client().execute(PutInferenceModelAction.INSTANCE, request).actionGet(); + assertEquals("test_service", response.getModel().getService()); + + assertThat(response.getModel().getServiceSettings(), instanceOf(TestInferenceServicePlugin.TestServiceSettings.class)); + var serviceSettings = (TestInferenceServicePlugin.TestServiceSettings) response.getModel().getServiceSettings(); + assertEquals("my_model", serviceSettings.model()); + assertEquals("abc64", serviceSettings.apiKey()); + + assertThat(response.getModel().getTaskSettings(), instanceOf(TestInferenceServicePlugin.TestTaskSettings.class)); + var taskSettings = (TestInferenceServicePlugin.TestTaskSettings) response.getModel().getTaskSettings(); + assertEquals(3, (int) taskSettings.temperature()); + + return response.getModel(); + } + + public Model getModel(String modelId, TaskType taskType) { + var response = client().execute(GetInferenceModelAction.INSTANCE, new GetInferenceModelAction.Request(modelId, taskType.toString())) + .actionGet(); + return response.getModel(); + } + + private void inferOnMockService(String modelId, TaskType taskType, String input) { + var response = client().execute(InferenceAction.INSTANCE, new InferenceAction.Request(taskType, modelId, input, Map.of())) + .actionGet(); + if (taskType == TaskType.SPARSE_EMBEDDING) { + assertThat(response.getResult(), instanceOf(TextExpansionResults.class)); + var teResult = (TextExpansionResults) response.getResult(); + assertThat(teResult.getWeightedTokens(), not(empty())); + } else { + fail("test with task type [" + taskType + "] are not supported yet"); + } + } + + private void assertModelsAreEqual(Model model1, Model model2) { + // The test can't rely on Model::equals as the specific subclass + // may be different. Model loses information about it's implemented + // subtype when it is streamed across the wire. + assertEquals(model1.getModelId(), model2.getModelId()); + assertEquals(model1.getService(), model2.getService()); + assertEquals(model1.getTaskType(), model2.getTaskType()); + + // TaskSettings and Service settings are named writables so + // the actual implementing class type is not lost when streamed \ + assertEquals(model1.getServiceSettings(), model2.getServiceSettings()); + assertEquals(model1.getTaskSettings(), model2.getTaskSettings()); + } +} diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java index fbb6bb7e316ff..a400f84e3c2ec 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java @@ -9,15 +9,15 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.ServiceSettings; +import org.elasticsearch.inference.TaskSettings; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xpack.inference.InferencePlugin; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.ServiceSettings; -import org.elasticsearch.xpack.inference.TaskSettings; -import org.elasticsearch.xpack.inference.TaskType; import org.elasticsearch.xpack.inference.UnparsedModel; import org.elasticsearch.xpack.inference.registry.ModelRegistry; import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeModel; diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/TestInferenceServicePlugin.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/TestInferenceServicePlugin.java new file mode 100644 index 0000000000000..b9b0d68054ef5 --- /dev/null +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/TestInferenceServicePlugin.java @@ -0,0 +1,238 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.inference.integration; + +import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.common.ValidationException; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; +import org.elasticsearch.inference.InferenceService; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.ServiceSettings; +import org.elasticsearch.inference.TaskSettings; +import org.elasticsearch.inference.TaskType; +import org.elasticsearch.plugins.InferenceServicePlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResultsTests; +import org.elasticsearch.xpack.inference.services.MapParsingUtils; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.xpack.inference.services.MapParsingUtils.removeFromMapOrThrowIfNull; +import static org.elasticsearch.xpack.inference.services.MapParsingUtils.throwIfNotEmptyMap; + +public class TestInferenceServicePlugin extends Plugin implements InferenceServicePlugin { + + @Override + public List getInferenceServiceFactories() { + return List.of(TestInferenceService::new); + } + + @Override + public List getInferenceServiceNamedWriteables() { + return List.of( + new NamedWriteableRegistry.Entry(ServiceSettings.class, TestServiceSettings.NAME, TestServiceSettings::new), + new NamedWriteableRegistry.Entry(TaskSettings.class, TestTaskSettings.NAME, TestTaskSettings::new) + ); + } + + public class TestInferenceService implements InferenceService { + + private static final String NAME = "test_service"; + + public static TestServiceModel parseConfig( + boolean throwOnUnknownFields, + String modelId, + TaskType taskType, + Map settings + ) { + Map serviceSettingsMap = removeFromMapOrThrowIfNull(settings, Model.SERVICE_SETTINGS); + var serviceSettings = TestServiceSettings.fromMap(serviceSettingsMap); + + Map taskSettingsMap; + // task settings are optional + if (settings.containsKey(Model.TASK_SETTINGS)) { + taskSettingsMap = removeFromMapOrThrowIfNull(settings, Model.TASK_SETTINGS); + } else { + taskSettingsMap = Map.of(); + } + + var taskSettings = TestTaskSettings.fromMap(taskSettingsMap); + + if (throwOnUnknownFields) { + throwIfNotEmptyMap(settings, NAME); + throwIfNotEmptyMap(serviceSettingsMap, NAME); + throwIfNotEmptyMap(taskSettingsMap, NAME); + } + + return new TestServiceModel(modelId, taskType, NAME, serviceSettings, taskSettings); + } + + public TestInferenceService(InferenceServicePlugin.InferenceServiceFactoryContext context) { + + } + + @Override + public String name() { + return NAME; + } + + @Override + public TestServiceModel parseConfigStrict(String modelId, TaskType taskType, Map config) { + return parseConfig(true, modelId, taskType, config); + } + + @Override + public TestServiceModel parseConfigLenient(String modelId, TaskType taskType, Map config) { + return parseConfig(false, modelId, taskType, config); + } + + @Override + public void infer(Model model, String input, Map taskSettings, ActionListener listener) { + switch (model.getTaskType()) { + case SPARSE_EMBEDDING -> listener.onResponse(TextExpansionResultsTests.createRandomResults(1, 10)); + default -> listener.onFailure( + new ElasticsearchStatusException( + TaskType.unsupportedTaskTypeErrorMsg(model.getTaskType(), NAME), + RestStatus.BAD_REQUEST + ) + ); + } + + } + + @Override + public void start(Model model, ActionListener listener) { + listener.onResponse(true); + } + } + + public static class TestServiceModel extends Model { + + public TestServiceModel( + String modelId, + TaskType taskType, + String service, + TestServiceSettings serviceSettings, + TestTaskSettings taskSettings + ) { + super(modelId, taskType, service, serviceSettings, taskSettings); + } + + @Override + public TestServiceSettings getServiceSettings() { + return (TestServiceSettings) super.getServiceSettings(); + } + + @Override + public TestTaskSettings getTaskSettings() { + return (TestTaskSettings) super.getTaskSettings(); + } + } + + public record TestServiceSettings(String model, String apiKey) implements ServiceSettings { + + private static final String NAME = "test_service_settings"; + + public static TestServiceSettings fromMap(Map map) { + ValidationException validationException = new ValidationException(); + + String model = MapParsingUtils.removeAsType(map, "model", String.class); + String apiKey = MapParsingUtils.removeAsType(map, "api_key", String.class); + + if (model == null) { + validationException.addValidationError(MapParsingUtils.missingSettingErrorMsg("model", Model.SERVICE_SETTINGS)); + } + if (apiKey == null) { + validationException.addValidationError(MapParsingUtils.missingSettingErrorMsg("api_key", Model.SERVICE_SETTINGS)); + } + + if (validationException.validationErrors().isEmpty() == false) { + throw validationException; + } + + return new TestServiceSettings(model, apiKey); + } + + public TestServiceSettings(StreamInput in) throws IOException { + this(in.readString(), in.readString()); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("model", model); + builder.field("api_key", apiKey); + builder.endObject(); + return builder; + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public TransportVersion getMinimalSupportedVersion() { + return TransportVersion.current(); // fine for these tests but will not work for cluster upgrade tests + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(model); + out.writeString(apiKey); + } + } + + public record TestTaskSettings(Integer temperature) implements TaskSettings { + + private static final String NAME = "test_task_settings"; + + public static TestTaskSettings fromMap(Map map) { + Integer temperature = MapParsingUtils.removeAsType(map, "temperature", Integer.class); + return new TestTaskSettings(temperature); + } + + public TestTaskSettings(StreamInput in) throws IOException { + this(in.readOptionalVInt()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeOptionalVInt(temperature); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (temperature != null) { + builder.field("temperature", temperature); + } + builder.endObject(); + return builder; + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public TransportVersion getMinimalSupportedVersion() { + return TransportVersion.current(); // fine for these tests but will not work for cluster upgrade tests + } + } +} diff --git a/x-pack/plugin/inference/src/main/java/module-info.java b/x-pack/plugin/inference/src/main/java/module-info.java index e80d828e4e48d..b21f919bbdc8a 100644 --- a/x-pack/plugin/inference/src/main/java/module-info.java +++ b/x-pack/plugin/inference/src/main/java/module-info.java @@ -17,6 +17,5 @@ exports org.elasticsearch.xpack.inference.rest; exports org.elasticsearch.xpack.inference.action; exports org.elasticsearch.xpack.inference.registry; - exports org.elasticsearch.xpack.inference.results; exports org.elasticsearch.xpack.inference; } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceNamedWriteablesProvider.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceNamedWriteablesProvider.java index 3bbc0a53a9973..3ef93c6c275d8 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceNamedWriteablesProvider.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferenceNamedWriteablesProvider.java @@ -8,8 +8,8 @@ package org.elasticsearch.xpack.inference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.xpack.inference.results.InferenceResult; -import org.elasticsearch.xpack.inference.results.SparseEmbeddingResult; +import org.elasticsearch.inference.ServiceSettings; +import org.elasticsearch.inference.TaskSettings; import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeServiceSettings; import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeTaskSettings; @@ -31,11 +31,6 @@ public static List getNamedWriteables() { new NamedWriteableRegistry.Entry(TaskSettings.class, ElserMlNodeTaskSettings.NAME, ElserMlNodeTaskSettings::new) ); - // Inference results - namedWriteables.add( - new NamedWriteableRegistry.Entry(InferenceResult.class, SparseEmbeddingResult.NAME, SparseEmbeddingResult::new) - ); - return namedWriteables; } } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java index a5b0754a7f178..ba0f1b142a799 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java @@ -27,6 +27,7 @@ import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.ActionPlugin; +import org.elasticsearch.plugins.InferenceServicePlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.repositories.RepositoriesService; @@ -47,7 +48,6 @@ import org.elasticsearch.xpack.inference.action.TransportInferenceAction; import org.elasticsearch.xpack.inference.action.TransportPutInferenceModelAction; import org.elasticsearch.xpack.inference.registry.ModelRegistry; -import org.elasticsearch.xpack.inference.registry.ServiceRegistry; import org.elasticsearch.xpack.inference.rest.RestDeleteInferenceModelAction; import org.elasticsearch.xpack.inference.rest.RestGetInferenceModelAction; import org.elasticsearch.xpack.inference.rest.RestInferenceAction; @@ -55,11 +55,10 @@ import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeService; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.function.Supplier; -public class InferencePlugin extends Plugin implements ActionPlugin, SystemIndexPlugin { +public class InferencePlugin extends Plugin implements ActionPlugin, InferenceServicePlugin, SystemIndexPlugin { public static final String NAME = "inference"; @@ -75,16 +74,6 @@ public class InferencePlugin extends Plugin implements ActionPlugin, SystemIndex ); } - @Override - public List getNamedWriteables() { - return InferenceNamedWriteablesProvider.getNamedWriteables(); - } - - @Override - public List getNamedXContent() { - return Collections.emptyList(); - } - @Override public List getRestHandlers( Settings settings, @@ -121,8 +110,7 @@ public Collection createComponents( IndicesService indicesService ) { ModelRegistry modelRegistry = new ModelRegistry(client); - ServiceRegistry serviceRegistry = new ServiceRegistry(new ElserMlNodeService(client)); - return List.of(modelRegistry, serviceRegistry); + return List.of(modelRegistry); } @Override @@ -155,4 +143,14 @@ public String getFeatureName() { public String getFeatureDescription() { return "Inference plugin for managing inference services and inference"; } + + @Override + public List getInferenceServiceFactories() { + return List.of(ElserMlNodeService::new); + } + + @Override + public List getInferenceServiceNamedWriteables() { + return InferenceNamedWriteablesProvider.getNamedWriteables(); + } } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/UnparsedModel.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/UnparsedModel.java index 29b4accf1f4f0..b6dd41df174e5 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/UnparsedModel.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/UnparsedModel.java @@ -8,6 +8,8 @@ package org.elasticsearch.xpack.inference; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.rest.RestStatus; import java.util.Map; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/DeleteInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/DeleteInferenceModelAction.java index 2ae1a33f8f5e3..4062946935b2e 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/DeleteInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/DeleteInferenceModelAction.java @@ -13,7 +13,7 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xpack.inference.TaskType; +import org.elasticsearch.inference.TaskType; import java.io.IOException; import java.util.Objects; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/GetInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/GetInferenceModelAction.java index a80ed84d8a6ed..7e47d3d93e3ca 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/GetInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/GetInferenceModelAction.java @@ -12,7 +12,7 @@ import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xpack.inference.TaskType; +import org.elasticsearch.inference.TaskType; import java.io.IOException; import java.util.Objects; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/InferenceAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/InferenceAction.java index 2b95e6153a360..7938c2abd8d99 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/InferenceAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/InferenceAction.java @@ -14,14 +14,14 @@ import org.elasticsearch.action.ActionType; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; -import org.elasticsearch.xpack.inference.TaskType; -import org.elasticsearch.xpack.inference.results.InferenceResult; import java.io.IOException; import java.util.Map; @@ -168,15 +168,19 @@ public Request build() { public static class Response extends ActionResponse implements ToXContentObject { - private final InferenceResult result; + private final InferenceResults result; - public Response(InferenceResult result) { + public Response(InferenceResults result) { this.result = result; } public Response(StreamInput in) throws IOException { super(in); - result = in.readNamedWriteable(InferenceResult.class); + result = in.readNamedWriteable(InferenceResults.class); + } + + public InferenceResults getResult() { + return result; } @Override diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/PutInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/PutInferenceModelAction.java index 6ec020e6b479a..6c59fc89fd152 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/PutInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/PutInferenceModelAction.java @@ -15,11 +15,11 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.TaskType; import java.io.IOException; import java.util.Objects; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportGetInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportGetInferenceModelAction.java index f11e6101e1e2a..1e208e83985cb 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportGetInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportGetInferenceModelAction.java @@ -12,26 +12,26 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.inference.InferenceServiceRegistry; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.inference.UnparsedModel; import org.elasticsearch.xpack.inference.registry.ModelRegistry; -import org.elasticsearch.xpack.inference.registry.ServiceRegistry; public class TransportGetInferenceModelAction extends HandledTransportAction< GetInferenceModelAction.Request, PutInferenceModelAction.Response> { private final ModelRegistry modelRegistry; - private final ServiceRegistry serviceRegistry; + private final InferenceServiceRegistry serviceRegistry; @Inject public TransportGetInferenceModelAction( TransportService transportService, ActionFilters actionFilters, ModelRegistry modelRegistry, - ServiceRegistry serviceRegistry + InferenceServiceRegistry serviceRegistry ) { super(GetInferenceModelAction.NAME, transportService, actionFilters, GetInferenceModelAction.Request::new); this.modelRegistry = modelRegistry; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportInferenceAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportInferenceAction.java index 42fa61b406e9e..aab8ed98f4241 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportInferenceAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportInferenceAction.java @@ -11,33 +11,27 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.inference.InferenceService; +import org.elasticsearch.inference.InferenceServiceRegistry; +import org.elasticsearch.inference.Model; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.inference.Model; import org.elasticsearch.xpack.inference.UnparsedModel; import org.elasticsearch.xpack.inference.registry.ModelRegistry; -import org.elasticsearch.xpack.inference.registry.ServiceRegistry; -import org.elasticsearch.xpack.inference.services.InferenceService; public class TransportInferenceAction extends HandledTransportAction { private final ModelRegistry modelRegistry; - private final ServiceRegistry serviceRegistry; + private final InferenceServiceRegistry serviceRegistry; @Inject public TransportInferenceAction( - Settings settings, TransportService transportService, - ClusterService clusterService, - ThreadPool threadPool, ActionFilters actionFilters, ModelRegistry modelRegistry, - ServiceRegistry serviceRegistry + InferenceServiceRegistry serviceRegistry ) { super(InferenceAction.NAME, transportService, actionFilters, InferenceAction.Request::new); this.modelRegistry = modelRegistry; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java index 5b948fc94d36e..8ab09bafbd248 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/TransportPutInferenceModelAction.java @@ -19,16 +19,16 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.inference.InferenceService; +import org.elasticsearch.inference.InferenceServiceRegistry; +import org.elasticsearch.inference.Model; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParserConfiguration; -import org.elasticsearch.xpack.inference.Model; import org.elasticsearch.xpack.inference.registry.ModelRegistry; -import org.elasticsearch.xpack.inference.registry.ServiceRegistry; -import org.elasticsearch.xpack.inference.services.InferenceService; import java.io.IOException; import java.util.Map; @@ -38,7 +38,7 @@ public class TransportPutInferenceModelAction extends TransportMasterNodeAction< PutInferenceModelAction.Response> { private final ModelRegistry modelRegistry; - private final ServiceRegistry serviceRegistry; + private final InferenceServiceRegistry serviceRegistry; @Inject public TransportPutInferenceModelAction( @@ -48,7 +48,7 @@ public TransportPutInferenceModelAction( ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, ModelRegistry modelRegistry, - ServiceRegistry serviceRegistry + InferenceServiceRegistry serviceRegistry ) { super( PutInferenceModelAction.NAME, diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ModelRegistry.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ModelRegistry.java index 5ad9554959a27..4403ec53e7a13 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ModelRegistry.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ModelRegistry.java @@ -25,6 +25,7 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.DeleteByQueryRequest; +import org.elasticsearch.inference.Model; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContentObject; @@ -32,7 +33,6 @@ import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.inference.InferenceIndex; -import org.elasticsearch.xpack.inference.Model; import java.io.IOException; import java.util.Map; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ServiceRegistry.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ServiceRegistry.java deleted file mode 100644 index 8767630f5625b..0000000000000 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/registry/ServiceRegistry.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.inference.registry; - -import org.elasticsearch.xpack.inference.services.InferenceService; -import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeService; - -import java.util.Optional; - -public class ServiceRegistry { - - ElserMlNodeService elserService; - - public ServiceRegistry(ElserMlNodeService elserService) { - this.elserService = elserService; - } - - public Optional getService(String name) { - if (name.equals(ElserMlNodeService.NAME)) { - return Optional.of(elserService); - } - - return Optional.empty(); - } - -} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/InferenceResult.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/InferenceResult.java deleted file mode 100644 index 8d8351dbe38d3..0000000000000 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/InferenceResult.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.inference.results; - -import org.elasticsearch.common.io.stream.VersionedNamedWriteable; -import org.elasticsearch.xcontent.ToXContentFragment; - -public interface InferenceResult extends ToXContentFragment, VersionedNamedWriteable {} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResult.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResult.java deleted file mode 100644 index 3f84c91b055a1..0000000000000 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResult.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.inference.results; - -import org.elasticsearch.TransportVersion; -import org.elasticsearch.TransportVersions; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; - -public class SparseEmbeddingResult implements InferenceResult { - - public static final String NAME = "sparse_embedding_result"; - - private final List weightedTokens; - - public SparseEmbeddingResult(List weightedTokens) { - this.weightedTokens = weightedTokens; - } - - public SparseEmbeddingResult(StreamInput in) throws IOException { - this.weightedTokens = in.readCollectionAsImmutableList(TextExpansionResults.WeightedToken::new); - } - - public List getWeightedTokens() { - return weightedTokens; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject("sparse_embedding"); - for (var weightedToken : weightedTokens) { - weightedToken.toXContent(builder, params); - } - builder.endObject(); - return builder; - } - - @Override - public String getWriteableName() { - return NAME; - } - - @Override - public TransportVersion getMinimalSupportedVersion() { - return TransportVersions.V_8_500_074; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeCollection(weightedTokens); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SparseEmbeddingResult that = (SparseEmbeddingResult) o; - return Objects.equals(weightedTokens, that.weightedTokens); - } - - @Override - public int hashCode() { - return Objects.hash(weightedTokens); - } - - @Override - public String toString() { - return Strings.toString(this); - } -} diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/MapParsingUtils.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/MapParsingUtils.java index c2b4986b84dc5..31228b645cff2 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/MapParsingUtils.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/MapParsingUtils.java @@ -54,6 +54,12 @@ public static Map removeFromMapOrThrowIfNull(Map return value; } + public static void throwIfNotEmptyMap(Map settingsMap, String serviceName) { + if (settingsMap.isEmpty() == false) { + throw MapParsingUtils.unknownSettingsError(settingsMap, serviceName); + } + } + public static ElasticsearchStatusException unknownSettingsError(Map config, String serviceName) { // TOOD map as JSON return new ElasticsearchStatusException( diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeModel.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeModel.java index 499a336c5d1a6..6c7e36e5d81ee 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeModel.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeModel.java @@ -7,8 +7,8 @@ package org.elasticsearch.xpack.inference.services.elser; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.TaskType; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; public class ElserMlNodeModel extends Model { diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeService.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeService.java index 602048f2e3e76..45acc467b047b 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeService.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeService.java @@ -9,27 +9,26 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.OriginSettingClient; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; +import org.elasticsearch.inference.InferenceService; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; +import org.elasticsearch.plugins.InferenceServicePlugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.ml.action.InferTrainedModelDeploymentAction; import org.elasticsearch.xpack.core.ml.action.StartTrainedModelDeploymentAction; import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TextExpansionConfigUpdate; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.TaskType; -import org.elasticsearch.xpack.inference.results.InferenceResult; -import org.elasticsearch.xpack.inference.results.SparseEmbeddingResult; -import org.elasticsearch.xpack.inference.services.InferenceService; -import org.elasticsearch.xpack.inference.services.MapParsingUtils; import java.util.List; import java.util.Map; import static org.elasticsearch.xpack.core.ml.inference.assignment.AllocationStatus.State.STARTED; import static org.elasticsearch.xpack.inference.services.MapParsingUtils.removeFromMapOrThrowIfNull; +import static org.elasticsearch.xpack.inference.services.MapParsingUtils.throwIfNotEmptyMap; public class ElserMlNodeService implements InferenceService { @@ -57,9 +56,9 @@ public static ElserMlNodeModel parseConfig( var taskSettings = taskSettingsFromMap(taskType, taskSettingsMap); if (throwOnUnknownFields) { - throwIfNotEmptyMap(settings); - throwIfNotEmptyMap(serviceSettingsMap); - throwIfNotEmptyMap(taskSettingsMap); + throwIfNotEmptyMap(settings, NAME); + throwIfNotEmptyMap(serviceSettingsMap, NAME); + throwIfNotEmptyMap(taskSettingsMap, NAME); } return new ElserMlNodeModel(modelId, taskType, NAME, serviceSettings, taskSettings); @@ -67,8 +66,8 @@ public static ElserMlNodeModel parseConfig( private final OriginSettingClient client; - public ElserMlNodeService(Client client) { - this.client = new OriginSettingClient(client, ClientHelper.INFERENCE_ORIGIN); + public ElserMlNodeService(InferenceServicePlugin.InferenceServiceFactoryContext context) { + this.client = new OriginSettingClient(context.client(), ClientHelper.INFERENCE_ORIGIN); } @Override @@ -89,7 +88,7 @@ public void start(Model model, ActionListener listener) { } if (model.getTaskType() != TaskType.SPARSE_EMBEDDING) { - listener.onFailure(new IllegalStateException(unsupportedTaskTypeErrorMsg(model.getTaskType()))); + listener.onFailure(new IllegalStateException(TaskType.unsupportedTaskTypeErrorMsg(model.getTaskType(), NAME))); return; } @@ -109,11 +108,13 @@ public void start(Model model, ActionListener listener) { } @Override - public void infer(Model model, String input, Map requestTaskSettings, ActionListener listener) { + public void infer(Model model, String input, Map taskSettings, ActionListener listener) { // No task settings to override with requestTaskSettings if (model.getTaskType() != TaskType.SPARSE_EMBEDDING) { - listener.onFailure(new ElasticsearchStatusException(unsupportedTaskTypeErrorMsg(model.getTaskType()), RestStatus.BAD_REQUEST)); + listener.onFailure( + new ElasticsearchStatusException(TaskType.unsupportedTaskTypeErrorMsg(model.getTaskType(), NAME), RestStatus.BAD_REQUEST) + ); return; } @@ -125,8 +126,7 @@ public void infer(Model model, String input, Map requestTaskSett ); client.execute(InferTrainedModelDeploymentAction.INSTANCE, request, ActionListener.wrap(inferenceResult -> { var textExpansionResult = (TextExpansionResults) inferenceResult.getResults().get(0); - var sparseEmbeddingResult = new SparseEmbeddingResult(textExpansionResult.getWeightedTokens()); - listener.onResponse(sparseEmbeddingResult); + listener.onResponse(textExpansionResult); }, listener::onFailure)); } @@ -136,7 +136,7 @@ private static ElserMlNodeServiceSettings serviceSettingsFromMap(Map config) { if (taskType != TaskType.SPARSE_EMBEDDING) { - throw new ElasticsearchStatusException(unsupportedTaskTypeErrorMsg(taskType), RestStatus.BAD_REQUEST); + throw new ElasticsearchStatusException(TaskType.unsupportedTaskTypeErrorMsg(taskType, NAME), RestStatus.BAD_REQUEST); } // no config options yet @@ -147,14 +147,4 @@ private static ElserMlNodeTaskSettings taskSettingsFromMap(TaskType taskType, Ma public String name() { return NAME; } - - private static void throwIfNotEmptyMap(Map settingsMap) { - if (settingsMap.isEmpty() == false) { - throw MapParsingUtils.unknownSettingsError(settingsMap, NAME); - } - } - - private static String unsupportedTaskTypeErrorMsg(TaskType taskType) { - return "The [" + NAME + "] service does not support task type [" + taskType + "]"; - } } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceSettings.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceSettings.java index 1d6a5106a1959..1314e6eab4f25 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceSettings.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceSettings.java @@ -12,9 +12,9 @@ import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.ServiceSettings; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.ServiceSettings; import org.elasticsearch.xpack.inference.services.MapParsingUtils; import java.io.IOException; diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeTaskSettings.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeTaskSettings.java index f4c75683783f0..c1d84af5b5fbe 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeTaskSettings.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeTaskSettings.java @@ -11,8 +11,8 @@ import org.elasticsearch.TransportVersions; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.TaskSettings; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.inference.TaskSettings; import java.io.IOException; import java.util.Objects; diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/ModelTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/ModelTests.java index 57f4c0650b932..778f4703767a6 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/ModelTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/ModelTests.java @@ -9,6 +9,10 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.ServiceSettings; +import org.elasticsearch.inference.TaskSettings; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeServiceSettingsTests; import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeTaskSettings; diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/GetInferenceModelRequestTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/GetInferenceModelRequestTests.java index 22a4981e092dc..0b30dc9021038 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/GetInferenceModelRequestTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/GetInferenceModelRequestTests.java @@ -8,8 +8,8 @@ package org.elasticsearch.xpack.inference.action; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.test.AbstractWireSerializingTestCase; -import org.elasticsearch.xpack.inference.TaskType; public class GetInferenceModelRequestTests extends AbstractWireSerializingTestCase { diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionRequestTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionRequestTests.java index f937ba03ae864..3e1bea0051656 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionRequestTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionRequestTests.java @@ -9,8 +9,8 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.test.AbstractWireSerializingTestCase; -import org.elasticsearch.xpack.inference.TaskType; import java.io.IOException; import java.util.HashMap; diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionResponseTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionResponseTests.java index 13896607fe9ab..795923e56c6bb 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionResponseTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/InferenceActionResponseTests.java @@ -10,16 +10,22 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.xpack.core.ml.inference.MlInferenceNamedXContentProvider; +import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResultsTests; import org.elasticsearch.xpack.inference.InferenceNamedWriteablesProvider; -import org.elasticsearch.xpack.inference.results.SparseEmbeddingResultTests; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public class InferenceActionResponseTests extends AbstractWireSerializingTestCase { @Override protected NamedWriteableRegistry getNamedWriteableRegistry() { - return new NamedWriteableRegistry(InferenceNamedWriteablesProvider.getNamedWriteables()); + List entries = new ArrayList<>(); + entries.addAll(new MlInferenceNamedXContentProvider().getNamedWriteables()); + entries.addAll(InferenceNamedWriteablesProvider.getNamedWriteables()); + return new NamedWriteableRegistry(entries); } @Override @@ -29,7 +35,7 @@ protected Writeable.Reader instanceReader() { @Override protected InferenceAction.Response createTestInstance() { - return new InferenceAction.Response(SparseEmbeddingResultTests.createRandomResult()); + return new InferenceAction.Response(TextExpansionResultsTests.createRandomResults()); } @Override diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/PutInferenceModelRequestTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/PutInferenceModelRequestTests.java index 770faf19585c5..9aefea9a942db 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/PutInferenceModelRequestTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/action/PutInferenceModelRequestTests.java @@ -8,9 +8,9 @@ package org.elasticsearch.xpack.inference.action; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.inference.TaskType; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.xpack.inference.TaskType; public class PutInferenceModelRequestTests extends AbstractWireSerializingTestCase { @Override diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/registry/ServiceRegistryTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/registry/ServiceRegistryTests.java deleted file mode 100644 index 492fb29f910b8..0000000000000 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/registry/ServiceRegistryTests.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.inference.registry; - -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.inference.services.elser.ElserMlNodeService; - -import static org.mockito.Mockito.mock; - -public class ServiceRegistryTests extends ESTestCase { - - public void testGetService() { - ServiceRegistry registry = new ServiceRegistry(mock(ElserMlNodeService.class)); - var service = registry.getService(ElserMlNodeService.NAME); - assertTrue(service.isPresent()); - } - - public void testGetUnknownService() { - ServiceRegistry registry = new ServiceRegistry(mock(ElserMlNodeService.class)); - var service = registry.getService("foo"); - assertFalse(service.isPresent()); - } -} diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResultTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResultTests.java deleted file mode 100644 index 360dc3e97d141..0000000000000 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/results/SparseEmbeddingResultTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.xpack.inference.results; - -import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.test.AbstractWireSerializingTestCase; -import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; - -import java.util.ArrayList; -import java.util.List; - -public class SparseEmbeddingResultTests extends AbstractWireSerializingTestCase { - - public static SparseEmbeddingResult createRandomResult() { - int numTokens = randomIntBetween(1, 20); - List tokenList = new ArrayList<>(); - for (int i = 0; i < numTokens; i++) { - tokenList.add(new TextExpansionResults.WeightedToken(Integer.toString(i), (float) randomDoubleBetween(0.0, 5.0, false))); - } - return new SparseEmbeddingResult(tokenList); - } - - @Override - protected Writeable.Reader instanceReader() { - return SparseEmbeddingResult::new; - } - - @Override - protected SparseEmbeddingResult createTestInstance() { - return createRandomResult(); - } - - @Override - protected SparseEmbeddingResult mutateInstance(SparseEmbeddingResult instance) { - if (instance.getWeightedTokens().size() > 0) { - var tokens = instance.getWeightedTokens(); - return new SparseEmbeddingResult(tokens.subList(0, tokens.size() - 1)); - } else { - return new SparseEmbeddingResult(List.of(new TextExpansionResults.WeightedToken("a", 1.0f))); - } - } -} diff --git a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceTests.java b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceTests.java index bdbb4c545900c..0449c1b4a7d59 100644 --- a/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceTests.java +++ b/x-pack/plugin/inference/src/test/java/org/elasticsearch/xpack/inference/services/elser/ElserMlNodeServiceTests.java @@ -9,9 +9,10 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.inference.Model; +import org.elasticsearch.inference.TaskType; +import org.elasticsearch.plugins.InferenceServicePlugin; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.inference.Model; -import org.elasticsearch.xpack.inference.TaskType; import java.util.HashMap; import java.util.Map; @@ -35,7 +36,7 @@ public static Model randomModelConfig(String modelId, TaskType taskType) { } public void testParseConfigStrict() { - var service = new ElserMlNodeService(mock(Client.class)); + var service = createService(mock(Client.class)); var settings = new HashMap(); settings.put( @@ -59,7 +60,7 @@ public void testParseConfigStrict() { } public void testParseConfigStrictWithNoTaskSettings() { - var service = new ElserMlNodeService(mock(Client.class)); + var service = createService(mock(Client.class)); var settings = new HashMap(); settings.put( @@ -153,4 +154,9 @@ public void testParseConfigStrictWithUnknownSettings() { } } } + + private ElserMlNodeService createService(Client client) { + var context = new InferenceServicePlugin.InferenceServiceFactoryContext(client); + return new ElserMlNodeService(context); + } } diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/ModelInferenceActionIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/ModelInferenceActionIT.java index 80f671a2c9db3..b9ca3946412bc 100644 --- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/ModelInferenceActionIT.java +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/ModelInferenceActionIT.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.ml.integration; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.license.License; import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.action.InferModelAction; @@ -17,7 +18,6 @@ import org.elasticsearch.xpack.core.ml.inference.TrainedModelType; import org.elasticsearch.xpack.core.ml.inference.preprocessing.OneHotEncoding; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.SingleValueInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfigUpdate; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java index f0f8287ab0d78..43040b4f94823 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInferTrainedModelDeploymentAction.java @@ -17,11 +17,11 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ml.action.InferTrainedModelDeploymentAction; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.ml.inference.deployment.NlpInferenceInput; import org.elasticsearch.xpack.ml.inference.deployment.TrainedModelDeploymentTask; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInternalInferModelAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInternalInferModelAction.java index 17db1cbc5a234..c30b7a3232f57 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInternalInferModelAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportInternalInferModelAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.license.License; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; @@ -34,7 +35,6 @@ import org.elasticsearch.xpack.core.ml.inference.TrainedModelType; import org.elasticsearch.xpack.core.ml.inference.assignment.AssignmentState; import org.elasticsearch.xpack.core.ml.inference.assignment.TrainedModelAssignment; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.inference.ModelAliasMetadata; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InferencePipelineAggregator.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InferencePipelineAggregator.java index f68d1df0e9db4..c94eacad6fb86 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InferencePipelineAggregator.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InferencePipelineAggregator.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.ml.aggs.inference; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.AggregationReduceContext; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -18,7 +19,6 @@ import org.elasticsearch.search.aggregations.pipeline.AbstractPipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.support.AggregationPath; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfigUpdate; import org.elasticsearch.xpack.ml.inference.loadingservice.LocalModel; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregation.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregation.java index 59ef2cb6e8e3e..979c3ff46dcb3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregation.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregation.java @@ -9,10 +9,10 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.aggregations.AggregationReduceContext; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import java.io.IOException; import java.util.List; @@ -32,7 +32,7 @@ protected InternalInferenceAggregation(String name, Map metadata public InternalInferenceAggregation(StreamInput in) throws IOException { super(in); - inferenceResult = in.readNamedWriteable(InferenceResults.class); + inferenceResult = in.readNamedWriteable(org.elasticsearch.inference.InferenceResults.class); } public InferenceResults getInferenceResult() { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunner.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunner.java index 36bca38d9f587..168b0deda87d4 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunner.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunner.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.metrics.Max; @@ -28,7 +29,6 @@ import org.elasticsearch.tasks.TaskId; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.ml.dataframe.DestinationIndex; import org.elasticsearch.xpack.ml.dataframe.stats.DataCountsTracker; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java index e31f8d9992b64..fe8366065dac7 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentNodeService.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.component.LifecycleListener; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; @@ -40,7 +41,6 @@ import org.elasticsearch.xpack.core.ml.inference.assignment.RoutingStateAndReason; import org.elasticsearch.xpack.core.ml.inference.assignment.TrainedModelAssignment; import org.elasticsearch.xpack.core.ml.inference.persistence.InferenceIndexConstants; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.ml.MachineLearning; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java index 21bd66e6f35ea..03f34dacb1faf 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.query.IdsQueryBuilder; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.SearchHit; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.threadpool.ThreadPool; @@ -32,7 +33,6 @@ import org.elasticsearch.xpack.core.ml.action.GetTrainedModelsAction; import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig; import org.elasticsearch.xpack.core.ml.inference.TrainedModelInput; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.IndexLocation; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchAction.java index bde15b4403f4c..474933236d196 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchAction.java @@ -12,10 +12,10 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskCancelledException; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/TrainedModelDeploymentTask.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/TrainedModelDeploymentTask.java index 025d0ecca00d2..ea06a0a0aba90 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/TrainedModelDeploymentTask.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/TrainedModelDeploymentTask.java @@ -14,6 +14,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.license.LicensedFeature; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.rest.RestStatus; @@ -22,7 +23,6 @@ import org.elasticsearch.xpack.core.ml.MlTasks; import org.elasticsearch.xpack.core.ml.action.StartTrainedModelDeploymentAction; import org.elasticsearch.xpack.core.ml.action.StartTrainedModelDeploymentAction.TaskParams; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfigUpdate; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessor.java index 8a4d9e569ea71..5d1c60964b8b9 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessor.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.ConfigurationUtils; import org.elasticsearch.ingest.IngestDocument; @@ -24,7 +25,6 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.action.InferModelAction; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfigUpdate; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.EmptyConfigUpdate; @@ -65,10 +65,10 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; +import static org.elasticsearch.inference.InferenceResults.MODEL_ID_RESULTS_FIELD; import static org.elasticsearch.ingest.IngestDocument.INGEST_KEY; import static org.elasticsearch.xpack.core.ClientHelper.ML_ORIGIN; import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.MODEL_ID_RESULTS_FIELD; public class InferenceProcessor extends AbstractProcessor { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModel.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModel.java index 1dc689215d74c..ffd70849d8f1c 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModel.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModel.java @@ -8,10 +8,10 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.license.License; import org.elasticsearch.xpack.core.ml.inference.TrainedModelInput; import org.elasticsearch.xpack.core.ml.inference.TrainedModelType; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfigUpdate; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingService.java index da2f97e283f2a..7a202df6fa744 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingService.java @@ -29,6 +29,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Strings; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.license.License; import org.elasticsearch.license.XPackLicenseState; @@ -38,7 +39,6 @@ import org.elasticsearch.xpack.core.ml.action.GetTrainedModelsAction; import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig; import org.elasticsearch.xpack.core.ml.inference.TrainedModelType; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.RegressionConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/FillMaskProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/FillMaskProcessor.java index d8f86eef2c4d0..d171795f86a54 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/FillMaskProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/FillMaskProcessor.java @@ -9,9 +9,9 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.common.ValidationException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.core.ml.inference.results.FillMaskResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.FillMaskConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NerProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NerProcessor.java index 160a53c861a8b..a0c8ddcba5125 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NerProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NerProcessor.java @@ -9,8 +9,8 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.common.ValidationException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.NerResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NerConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NlpTask.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NlpTask.java index 040b9fbedb289..273120bba09ea 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NlpTask.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/NlpTask.java @@ -10,7 +10,7 @@ import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.core.Releasable; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.Tokenization; import org.elasticsearch.xpack.ml.inference.nlp.tokenizers.NlpTokenizer; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/PassThroughProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/PassThroughProcessor.java index 03f1084e39184..417f13964f6cb 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/PassThroughProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/PassThroughProcessor.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.ml.inference.nlp; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.PyTorchPassThroughResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.ml.inference.nlp.tokenizers.NlpTokenizer; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/QuestionAnsweringProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/QuestionAnsweringProcessor.java index e15f0402db3b1..a7f7f2bb9f538 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/QuestionAnsweringProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/QuestionAnsweringProcessor.java @@ -9,8 +9,8 @@ import org.apache.lucene.util.PriorityQueue; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.QuestionAnsweringInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.QuestionAnsweringConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextClassificationProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextClassificationProcessor.java index 6fe1e66b8f6de..150ac184d246f 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextClassificationProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextClassificationProcessor.java @@ -8,8 +8,8 @@ package org.elasticsearch.xpack.ml.inference.nlp; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.NlpClassificationInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceHelpers; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextEmbeddingProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextEmbeddingProcessor.java index c11ea2005a05d..453b689d59cc0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextEmbeddingProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextEmbeddingProcessor.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.ml.inference.nlp; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TextEmbeddingResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.ml.inference.nlp.tokenizers.NlpTokenizer; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextExpansionProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextExpansionProcessor.java index 1983dd973c9ef..6483b9d9b3da9 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextExpansionProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextExpansionProcessor.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.ml.inference.nlp; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.ml.inference.nlp.tokenizers.NlpTokenizer; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextSimilarityProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextSimilarityProcessor.java index 1f296438c796c..0b4f9d8f897e0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextSimilarityProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/TextSimilarityProcessor.java @@ -8,8 +8,8 @@ package org.elasticsearch.xpack.ml.inference.nlp; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TextSimilarityInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TextSimilarityConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/ZeroShotClassificationProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/ZeroShotClassificationProcessor.java index eff6916d61609..a9422e66b16bc 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/ZeroShotClassificationProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/nlp/ZeroShotClassificationProcessor.java @@ -9,8 +9,8 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.NlpClassificationInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.TopClassEntry; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NlpConfig; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/rescorer/InferenceRescorer.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/rescorer/InferenceRescorer.java index ea7b064de8464..df3e0756ea39a 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/rescorer/InferenceRescorer.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/rescorer/InferenceRescorer.java @@ -16,9 +16,9 @@ import org.apache.lucene.search.TopDocs; import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.Strings; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.rescore.RescoreContext; import org.elasticsearch.search.rescore.Rescorer; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.ml.inference.loadingservice.LocalModel; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractor.java index 80bc6f208a501..48b570e927b15 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractor.java @@ -21,8 +21,8 @@ import java.util.Set; import java.util.function.Consumer; +import static org.elasticsearch.inference.InferenceResults.MODEL_ID_RESULTS_FIELD; import static org.elasticsearch.ingest.Pipeline.PROCESSORS_KEY; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.MODEL_ID_RESULTS_FIELD; import static org.elasticsearch.xpack.ml.inference.ingest.InferenceProcessor.TYPE; /** diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregationTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregationTests.java index 5dc7f43118dd7..016a89fe4e4b7 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregationTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/InternalInferenceAggregationTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.CollectionUtils; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.InvalidAggregationPathException; @@ -19,7 +20,6 @@ import org.elasticsearch.xpack.core.ml.inference.results.ClassificationFeatureImportance; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResultsTests; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RegressionFeatureImportance; import org.elasticsearch.xpack.core.ml.inference.results.RegressionInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.RegressionInferenceResultsTests; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/ParsedInference.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/ParsedInference.java index fa275b3f9f400..9b11fc166ef58 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/ParsedInference.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/inference/ParsedInference.java @@ -30,7 +30,7 @@ /** * There isn't enough information in toXContent representation of the - * {@link org.elasticsearch.xpack.core.ml.inference.results.InferenceResults} + * {@link org.elasticsearch.inference.InferenceResults} * objects to fully reconstruct them. In particular, depending on which * fields are written (result value, feature importance) it is not possible to * distinguish between a Regression result and a Classification result. diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunnerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunnerTests.java index 9885e1b46045b..b6d53b949ce15 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunnerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/dataframe/inference/InferenceRunnerTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.client.internal.Client; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.search.SearchHit; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; @@ -25,7 +26,6 @@ import org.elasticsearch.xpack.core.ml.dataframe.analyses.RegressionTests; import org.elasticsearch.xpack.core.ml.dataframe.stats.common.DataCounts; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig; import org.elasticsearch.xpack.ml.dataframe.stats.DataCountsTracker; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchActionTests.java index 181b6abd5b549..c937e9be24b01 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/InferencePyTorchActionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskAwareRequest; @@ -19,7 +20,6 @@ import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.PassThroughConfig; import org.elasticsearch.xpack.ml.inference.pytorch.process.PyTorchResultProcessor; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java index 0029276d20e6d..3854ad984467d 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.ingest.PipelineConfiguration; import org.elasticsearch.test.ESTestCase; @@ -34,7 +35,6 @@ import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.core.ml.MlConfigVersion; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.FillMaskConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.NerConfig; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModelTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModelTests.java index 4709925dbe7d7..faa64969fb6b2 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModelTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/LocalModelTests.java @@ -8,6 +8,7 @@ import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.TestIngestDocument; import org.elasticsearch.license.License; @@ -16,7 +17,6 @@ import org.elasticsearch.xpack.core.ml.inference.TrainedModelType; import org.elasticsearch.xpack.core.ml.inference.preprocessing.OneHotEncoding; import org.elasticsearch.xpack.core.ml.inference.results.ClassificationInferenceResults; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.SingleValueInferenceResults; import org.elasticsearch.xpack.core.ml.inference.results.WarningInferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; @@ -46,7 +46,7 @@ import java.util.Map; import java.util.TreeMap; -import static org.elasticsearch.xpack.core.ml.inference.results.InferenceResults.writeResult; +import static org.elasticsearch.inference.InferenceResults.writeResult; import static org.elasticsearch.xpack.core.ml.inference.trainedmodel.inference.EnsembleInferenceModelTests.serializeFromTrainedModel; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.closeTo; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingServiceTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingServiceTests.java index 644b619537741..2f4640cfa38dc 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingServiceTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/loadingservice/ModelLoadingServiceTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.ingest.PipelineConfiguration; import org.elasticsearch.license.XPackLicenseState; @@ -40,7 +41,6 @@ import org.elasticsearch.xpack.core.ml.action.GetTrainedModelsAction; import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig; import org.elasticsearch.xpack.core.ml.inference.TrainedModelInput; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ClassificationConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceStats; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.inference.InferenceDefinition; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractorTests.java index 2fa2149312ca2..f7b8b8a0967f9 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/InferenceProcessorInfoExtractorTests.java @@ -15,13 +15,13 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.inference.InferenceResults; import org.elasticsearch.ingest.IngestMetadata; import org.elasticsearch.ingest.PipelineConfiguration; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.xpack.core.ml.inference.results.InferenceResults; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.RegressionConfig; import org.elasticsearch.xpack.ml.inference.ingest.InferenceProcessor; From 6c40a96fb123853ed70eaac993919723d025bc46 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 27 Sep 2023 06:54:02 -0700 Subject: [PATCH 094/155] Collect and merge response headers in ESQL (#99926) It seems that our infrastructure does not merge response headers across multiple asynchronous calls. I can reproduce this issue using the TransportService. Response headers are not merged properly in this scenario: 1. The caller initiates two asynchronous calls, c1 and c2, which can involve network requests. 2. c1 responded with a warning in the header responses. We merge these response headers with the original ThreadContext of the calling thread and update the ThreadContext of the current thread (leaving the calling thread untouched). 3. c2 responded with no warning in the header responses. Since the original ThreadContext of the calling thread did not get updated after c1, as it's immutable, we won't be able to merge response headers between c1 and c2. 4. The caller receives a response from the responding thread of c2 without any warning. This PR manually collect and merge response headers in DriverRunner. I think we should generalize this pattern for Elasticsearch. --- .../compute/operator/DriverRunner.java | 38 ++++++++++++++++++- .../compute/operator/DriverTaskRunner.java | 2 +- .../operator/ForkingOperatorTestCase.java | 4 +- .../compute/operator/OperatorTestCase.java | 2 +- .../exchange/ExchangeServiceTests.java | 2 +- .../xpack/esql/lookup/EnrichLookupIT.java | 2 +- .../elasticsearch/xpack/esql/CsvTests.java | 2 +- 7 files changed, 44 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverRunner.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverRunner.java index c687ce7f864f1..788fc2887ebd9 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverRunner.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverRunner.java @@ -9,17 +9,29 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.util.concurrent.CountDown; +import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.Releasables; import org.elasticsearch.tasks.TaskCancelledException; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; /** * Run a set of drivers to completion. */ public abstract class DriverRunner { + private final ThreadContext threadContext; + + public DriverRunner(ThreadContext threadContext) { + this.threadContext = threadContext; + } + /** * Start a driver. */ @@ -30,8 +42,11 @@ public abstract class DriverRunner { */ public void runToCompletion(List drivers, ActionListener listener) { AtomicReference failure = new AtomicReference<>(); + AtomicArray>> responseHeaders = new AtomicArray<>(drivers.size()); CountDown counter = new CountDown(drivers.size()); - for (Driver driver : drivers) { + for (int i = 0; i < drivers.size(); i++) { + Driver driver = drivers.get(i); + int driverIndex = i; ActionListener driverListener = new ActionListener<>() { @Override public void onResponse(Void unused) { @@ -66,7 +81,9 @@ public void onFailure(Exception e) { } private void done() { + responseHeaders.setOnce(driverIndex, threadContext.getResponseHeaders()); if (counter.countDown()) { + mergeResponseHeaders(responseHeaders); for (Driver d : drivers) { if (d.status().status() == DriverStatus.Status.QUEUED) { d.close(); @@ -87,4 +104,23 @@ private void done() { start(driver, driverListener); } } + + private void mergeResponseHeaders(AtomicArray>> responseHeaders) { + final Map> merged = new HashMap<>(); + for (int i = 0; i < responseHeaders.length(); i++) { + final Map> resp = responseHeaders.get(i); + if (resp == null || resp.isEmpty()) { + continue; + } + for (Map.Entry> e : resp.entrySet()) { + // Use LinkedHashSet to retain the order of the values + merged.computeIfAbsent(e.getKey(), k -> new LinkedHashSet<>(e.getValue().size())).addAll(e.getValue()); + } + } + for (Map.Entry> e : merged.entrySet()) { + for (String v : e.getValue()) { + threadContext.addResponseHeader(e.getKey(), v); + } + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverTaskRunner.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverTaskRunner.java index 53d5a66de7b66..221be19cc2871 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverTaskRunner.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverTaskRunner.java @@ -42,7 +42,7 @@ public DriverTaskRunner(TransportService transportService, Executor executor) { } public void executeDrivers(Task parentTask, List drivers, Executor executor, ActionListener listener) { - var runner = new DriverRunner() { + var runner = new DriverRunner(transportService.getThreadPool().getThreadContext()) { @Override protected void start(Driver driver, ActionListener driverListener) { transportService.sendChildRequest( diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java index 1c12fbf4bcd52..9d1084fcc4cf3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java @@ -160,7 +160,7 @@ public final void testManyInitialManyPartialFinalRunner() { List results = new ArrayList<>(); List drivers = createDriversForInput(bigArrays, input, results, false /* no throwing ops */); - var runner = new DriverRunner() { + var runner = new DriverRunner(threadPool.getThreadContext()) { @Override protected void start(Driver driver, ActionListener listener) { Driver.start(threadPool.executor(ESQL_TEST_EXECUTOR), driver, between(1, 10000), listener); @@ -182,7 +182,7 @@ public final void testManyInitialManyPartialFinalRunnerThrowing() { List results = new ArrayList<>(); List drivers = createDriversForInput(bigArrays, input, results, true /* one throwing op */); - var runner = new DriverRunner() { + var runner = new DriverRunner(threadPool.getThreadContext()) { @Override protected void start(Driver driver, ActionListener listener) { Driver.start(threadPool.executor(ESQL_TEST_EXECUTOR), driver, between(1, 1000), listener); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java index 3cbab148e3073..3b2fac5271aa6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java @@ -194,7 +194,7 @@ public static void runDriver(List drivers) { getTestClass().getSimpleName(), new FixedExecutorBuilder(Settings.EMPTY, "esql", numThreads, 1024, "esql", EsExecutors.TaskTrackingConfig.DEFAULT) ); - var driverRunner = new DriverRunner() { + var driverRunner = new DriverRunner(threadPool.getThreadContext()) { @Override protected void start(Driver driver, ActionListener driverListener) { Driver.start(threadPool.executor("esql"), driver, between(1, 10000), driverListener); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java index 5b6b33ea0b80a..c94320a9d406a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java @@ -301,7 +301,7 @@ void runConcurrentTest( drivers.add(d); } PlainActionFuture future = new PlainActionFuture<>(); - new DriverRunner() { + new DriverRunner(threadPool.getThreadContext()) { @Override protected void start(Driver driver, ActionListener listener) { Driver.start(threadPool.executor(ESQL_TEST_EXECUTOR), driver, between(1, 10000), listener); diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java index d6611881f8546..fa5a1617e9d61 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/lookup/EnrichLookupIT.java @@ -141,7 +141,7 @@ public void testSimple() { DateFormatter dateFmt = DateFormatter.forPattern("yyyy-MM-dd"); - var runner = new DriverRunner() { + var runner = new DriverRunner(transportService.getThreadPool().getThreadContext()) { final Executor executor = transportService.getThreadPool().executor(EsqlPlugin.ESQL_THREAD_POOL_NAME); @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index 0f1d9257a6c0a..8a5b021addae5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -378,7 +378,7 @@ private ActualResults executePlan() throws Exception { Randomness.shuffle(drivers); } // Execute the driver - DriverRunner runner = new DriverRunner() { + DriverRunner runner = new DriverRunner(threadPool.getThreadContext()) { @Override protected void start(Driver driver, ActionListener driverListener) { Driver.start(threadPool.executor(ESQL_THREAD_POOL_NAME), driver, between(1, 1000), driverListener); From 2332688267b54b3167f84db6fa2c4dd48d6aa9be Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:12:11 +0100 Subject: [PATCH 095/155] ESQL: Add boolean to track block release (#99925) This commit adds a boolean state to blocks, allowing to tell is a block has been released. --- .../compute/data/BooleanArrayBlock.java | 4 + .../compute/data/BooleanVectorBlock.java | 4 + .../compute/data/BytesRefArrayBlock.java | 4 + .../compute/data/BytesRefVectorBlock.java | 4 + .../compute/data/DoubleArrayBlock.java | 4 + .../compute/data/DoubleVectorBlock.java | 4 + .../compute/data/FilterBooleanBlock.java | 3 + .../compute/data/FilterBytesRefBlock.java | 3 + .../compute/data/FilterDoubleBlock.java | 3 + .../compute/data/FilterIntBlock.java | 3 + .../compute/data/FilterLongBlock.java | 3 + .../compute/data/IntArrayBlock.java | 4 + .../compute/data/IntVectorBlock.java | 4 + .../compute/data/LongArrayBlock.java | 4 + .../compute/data/LongVectorBlock.java | 4 + .../compute/data/AbstractBlock.java | 7 + .../compute/data/AbstractFilterBlock.java | 5 + .../org/elasticsearch/compute/data/Block.java | 3 + .../compute/data/BlockUtils.java | 6 + .../compute/data/ConstantNullBlock.java | 4 + .../elasticsearch/compute/data/DocBlock.java | 4 + .../org/elasticsearch/compute/data/Page.java | 3 + .../compute/data/X-ArrayBlock.java.st | 4 + .../compute/data/X-FilterBlock.java.st | 3 + .../compute/data/X-VectorBlock.java.st | 4 + .../compute/operator/EvalOperator.java | 17 ++- .../compute/operator/ProjectOperator.java | 7 + .../compute/data/BasicBlockTests.java | 132 +++++++++++------- .../compute/data/BlockFactoryTests.java | 7 + .../compute/data/DocVectorTests.java | 18 +++ 30 files changed, 225 insertions(+), 54 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java index fafe1ce5a0416..d8a5e471aaf84 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java @@ -109,6 +109,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java index 1727b83360cab..a464d52482ced 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBlock.java @@ -72,6 +72,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java index 426731ac06798..e4ee70cd27a47 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java @@ -111,6 +111,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); Releasables.closeExpectNoException(values); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java index 3799e9c5b7ef7..3d761c6937c1b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBlock.java @@ -73,6 +73,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java index c8d7035b31d3b..b0de974a85c24 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java @@ -109,6 +109,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java index dba00f6b393a9..5c95bba795017 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBlock.java @@ -72,6 +72,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanBlock.java index 8b410fffc032b..62f09314cc0a2 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBooleanBlock.java @@ -126,6 +126,9 @@ private void appendValues(StringBuilder sb) { @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefBlock.java index cdc925c01f53e..7545f4ff49695 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterBytesRefBlock.java @@ -129,6 +129,9 @@ private void appendValues(StringBuilder sb) { @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleBlock.java index 2d02a8665b87f..9eb352bb345b1 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterDoubleBlock.java @@ -126,6 +126,9 @@ private void appendValues(StringBuilder sb) { @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntBlock.java index ffd72ec10d234..3d17cbcf41ded 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterIntBlock.java @@ -126,6 +126,9 @@ private void appendValues(StringBuilder sb) { @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongBlock.java index 85f72b1b0b44e..cfcf958e11be2 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/FilterLongBlock.java @@ -126,6 +126,9 @@ private void appendValues(StringBuilder sb) { @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java index 782e45a6df463..7a345941df019 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java @@ -109,6 +109,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java index b91c4c8dbeefa..d7051d533a13c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBlock.java @@ -72,6 +72,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java index 5d6c3d2931a85..21c6b445cd37d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java @@ -109,6 +109,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-ramBytesUsed(), true); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java index 3b3d13bf9c36a..e61b24bd0ec78 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBlock.java @@ -72,6 +72,10 @@ public String toString() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java index cf8a0d9a833ec..24d2bca58be7e 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlock.java @@ -23,6 +23,8 @@ abstract class AbstractBlock implements Block { protected final BlockFactory blockFactory; + protected boolean released = false; + /** * @param positionCount the number of values in this block */ @@ -94,4 +96,9 @@ public boolean areAllValuesNull() { public BlockFactory blockFactory() { return blockFactory; } + + @Override + public boolean isReleased() { + return released; + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java index b7ed14f955244..5f7d637069234 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractFilterBlock.java @@ -105,6 +105,11 @@ public BlockFactory blockFactory() { return block.blockFactory(); } + @Override + public boolean isReleased() { + return block.isReleased(); + } + private int mapPosition(int position) { assert assertPosition(position); return positions[position]; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index 1982c937f2a17..5b10a3a510de0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -61,6 +61,9 @@ public interface Block extends Accountable, NamedWriteable, Releasable { /** The block factory associated with this block. */ BlockFactory blockFactory(); + /** Tells if this block has been released. A block is released by calling its {@link Block#close()} method. */ + boolean isReleased(); + /** * Returns true if the value stored at the given position is null, false otherwise. * diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java index 4d31501b8e579..2ebbb771b5df1 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java @@ -122,6 +122,12 @@ public static Block[] fromList(List> list) { return Arrays.stream(wrappers).map(b -> b.builder.build()).toArray(Block[]::new); } + public static Block deepCopyOf(Block block) { + Block.Builder builder = block.elementType().newBlockBuilder(block.getPositionCount()); + builder.copyFrom(block, 0, block.getPositionCount()); + return builder.build(); + } + private static Class type(List> list, int i) { int p = 0; while (p < list.size()) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java index 7ad60d89ed72d..2da9cfeba09f0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java @@ -126,6 +126,10 @@ public String toString() { @Override public void close() { + if (isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; blockFactory.adjustBreaker(-ramBytesUsed(), true); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java index 433591be5b2bb..b21a956980f6a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java @@ -72,6 +72,10 @@ public long ramBytesUsed() { @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java index b5ea1aa6284ce..873565592dfaf 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java @@ -107,6 +107,9 @@ public B getBlock(int blockIndex) { } @SuppressWarnings("unchecked") B block = (B) blocks[blockIndex]; + if (block.isReleased()) { + throw new IllegalStateException("can't read released block [" + block + "]"); + } return block; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st index dd3a914eae9f7..10ff868c09806 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st @@ -132,6 +132,10 @@ $endif$ @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; $if(BytesRef)$ blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); Releasables.closeExpectNoException(values); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterBlock.java.st index a4c524422533d..46677533c7f45 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-FilterBlock.java.st @@ -150,6 +150,9 @@ $endif$ @Override public void close() { + if (block.isReleased()) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } Releasables.closeExpectNoException(block); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st index 0f97119d971a4..3499681dd49b6 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBlock.java.st @@ -80,6 +80,10 @@ $endif$ @Override public void close() { + if (released) { + throw new IllegalStateException("can't release already released block [" + this + "]"); + } + released = true; Releasables.closeExpectNoException(vector); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java index 3f307fb3fe115..aecbf07270a21 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java @@ -8,10 +8,13 @@ package org.elasticsearch.compute.operator; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockUtils; import org.elasticsearch.compute.data.Page; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; +import java.util.stream.IntStream; + /** * Evaluates a tree of functions for every position in the block, resulting in a * new block which is appended to the page. @@ -40,7 +43,9 @@ public EvalOperator(ExpressionEvaluator evaluator) { @Override protected Page process(Page page) { - return page.appendBlock(evaluator.eval(page)); + Block block = evaluator.eval(page); + block = maybeCopyBlock(page, block); + return page.appendBlock(block); } @Override @@ -53,6 +58,16 @@ public void close() { Releasables.closeExpectNoException(evaluator); } + /** Returns a copy of the give block, if the block appears in the page. */ + // TODO: this is a catch all, can be removed when we validate that evaluators always return copies + // for now it just looks like Attributes returns a reference? + static Block maybeCopyBlock(Page page, Block block) { + if (IntStream.range(0, page.getBlockCount()).mapToObj(page::getBlock).anyMatch(b -> b == block)) { + return BlockUtils.deepCopyOf(block); + } + return block; + } + /** * Evaluates an expression {@code a + b} or {@code log(c)} one {@link Page} at a time. */ diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java index a4b73d6277271..ec0edf5e3ed9c 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java @@ -60,6 +60,7 @@ protected Page process(Page page) { for (int i = 0; i < page.getBlockCount(); i++) { var block = page.getBlock(i); if (bs.get(i)) { + assertNotReleasing(blocksToRelease, block); blocks[b++] = block; } else { blocksToRelease.add(block); @@ -73,4 +74,10 @@ protected Page process(Page page) { public String toString() { return "ProjectOperator[mask = " + bs + ']'; } + + static void assertNotReleasing(List toRelease, Block toKeep) { + // verify by identity equality + assert toRelease.stream().anyMatch(r -> r == toKeep) == false + : "both releasing and keeping the same block: " + toRelease.stream().filter(r -> r == toKeep).toList(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index df16f0036c767..b6923926f3780 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.data; -import org.apache.lucene.util.Accountable; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.unit.ByteSizeValue; @@ -15,7 +14,6 @@ import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; -import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.test.ESTestCase; @@ -166,7 +164,11 @@ public void testIntBlock() { int pos = block.getInt(randomPosition(positionCount)); assertThat(pos, is(block.getInt(pos))); assertSingleValueDenseBlock(block); - releaseAndAssertBreaker(block); + + IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(1, blockFactory); + IntBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); + assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(block, copy); if (positionCount > 1) { assertNullValues( @@ -181,11 +183,6 @@ public void testIntBlock() { ); } - IntBlock.Builder blockBuilder = IntBlock.newBlockBuilder(1, blockFactory); - IntBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); - assertThat(copy, equalTo(block)); - releaseAndAssertBreaker(copy); - IntVector.Builder vectorBuilder = IntVector.newVectorBuilder( randomBoolean() ? randomIntBetween(1, positionCount) : positionCount, blockFactory @@ -193,7 +190,7 @@ public void testIntBlock() { IntStream.range(0, positionCount).forEach(vectorBuilder::appendInt); IntVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); - releaseAndAssertBreaker(vector); + releaseAndAssertBreaker(vector.asBlock()); } } @@ -238,7 +235,11 @@ public void testLongBlock() { int pos = (int) block.getLong(randomPosition(positionCount)); assertThat((long) pos, is(block.getLong(pos))); assertSingleValueDenseBlock(block); - releaseAndAssertBreaker(block); + + LongBlock.Builder blockBuilder = blockFactory.newLongBlockBuilder(1); + LongBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); + assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(block, copy); if (positionCount > 1) { assertNullValues( @@ -253,16 +254,13 @@ public void testLongBlock() { ); } - LongBlock.Builder blockBuilder = LongBlock.newBlockBuilder(1); - LongBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); - assertThat(copy, equalTo(block)); - - LongVector.Builder vectorBuilder = LongVector.newVectorBuilder( + LongVector.Builder vectorBuilder = blockFactory.newLongVectorBuilder( randomBoolean() ? randomIntBetween(1, positionCount) : positionCount ); LongStream.range(0, positionCount).forEach(vectorBuilder::appendLong); LongVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); + releaseAndAssertBreaker(vector.asBlock()); } } @@ -287,21 +285,19 @@ public void testConstantLongBlock() { } } - // TODO: continue to update the test, as above. - // Try to not complicate the "basic" test any more than necessary, but it already has great coverage - // for building all types of blocks!! - public void testDoubleBlock() { for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); DoubleBlock block; if (randomBoolean()) { final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - var blockBuilder = DoubleBlock.newBlockBuilder(builderEstimateSize); + var blockBuilder = blockFactory.newDoubleBlockBuilder(builderEstimateSize); LongStream.range(0, positionCount).asDoubleStream().forEach(blockBuilder::appendDouble); block = blockBuilder.build(); } else { - block = new DoubleArrayVector(LongStream.range(0, positionCount).asDoubleStream().toArray(), positionCount).asBlock(); + block = blockFactory.newDoubleArrayVector(LongStream.range(0, positionCount).asDoubleStream().toArray(), positionCount) + .asBlock(); } assertThat(positionCount, is(block.getPositionCount())); @@ -311,6 +307,11 @@ public void testDoubleBlock() { assertThat((double) pos, is(block.getDouble(pos))); assertSingleValueDenseBlock(block); + DoubleBlock.Builder blockBuilder = DoubleBlock.newBlockBuilder(1); + DoubleBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); + assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(block, copy); + if (positionCount > 1) { assertNullValues( positionCount, @@ -324,16 +325,13 @@ public void testDoubleBlock() { ); } - DoubleBlock.Builder blockBuilder = DoubleBlock.newBlockBuilder(1); - DoubleBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); - assertThat(copy, equalTo(block)); - - DoubleVector.Builder vectorBuilder = DoubleVector.newVectorBuilder( + DoubleVector.Builder vectorBuilder = blockFactory.newDoubleVectorBuilder( randomBoolean() ? randomIntBetween(1, positionCount) : positionCount ); IntStream.range(0, positionCount).mapToDouble(ii -> 1.0 / ii).forEach(vectorBuilder::appendDouble); DoubleVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); + releaseAndAssertBreaker(vector.asBlock()); } } @@ -343,15 +341,16 @@ public void testConstantDoubleBlock() { double value = randomDouble(); DoubleBlock block; if (randomBoolean()) { - block = DoubleBlock.newConstantBlockWith(value, positionCount); + block = DoubleBlock.newConstantBlockWith(value, positionCount, blockFactory); } else { - block = new ConstantDoubleVector(value, positionCount).asBlock(); + block = blockFactory.newConstantDoubleBlockWith(value, positionCount); } assertThat(positionCount, is(block.getPositionCount())); assertThat(value, is(block.getDouble(0))); assertThat(value, is(block.getDouble(positionCount - 1))); assertThat(value, is(block.getDouble(randomPosition(positionCount)))); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); } } @@ -370,13 +369,13 @@ public void testBytesRefBlock() { BytesRefBlock block; if (randomBoolean()) { final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - var blockBuilder = BytesRefBlock.newBlockBuilder(builderEstimateSize); + var blockBuilder = blockFactory.newBytesRefBlockBuilder(builderEstimateSize); Arrays.stream(values).map(obj -> randomBoolean() ? obj : BytesRef.deepCopyOf(obj)).forEach(blockBuilder::appendBytesRef); block = blockBuilder.build(); } else { BytesRefArray array = new BytesRefArray(0, BigArrays.NON_RECYCLING_INSTANCE); Arrays.stream(values).forEach(array::append); - block = new BytesRefArrayVector(array, positionCount).asBlock(); + block = blockFactory.newBytesRefArrayVector(array, positionCount).asBlock(); } assertThat(positionCount, is(block.getPositionCount())); @@ -388,6 +387,11 @@ public void testBytesRefBlock() { } assertSingleValueDenseBlock(block); + BytesRefBlock.Builder blockBuilder = BytesRefBlock.newBlockBuilder(1); + BytesRefBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); + assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(block, copy); + if (positionCount > 1) { assertNullValues( positionCount, @@ -402,22 +406,19 @@ public void testBytesRefBlock() { ); } - BytesRefBlock.Builder blockBuilder = BytesRefBlock.newBlockBuilder(1); - BytesRefBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); - assertThat(copy, equalTo(block)); - - BytesRefVector.Builder vectorBuilder = BytesRefVector.newVectorBuilder( + BytesRefVector.Builder vectorBuilder = blockFactory.newBytesRefVectorBuilder( randomBoolean() ? randomIntBetween(1, positionCount) : positionCount ); IntStream.range(0, positionCount).mapToObj(ii -> new BytesRef(randomAlphaOfLength(5))).forEach(vectorBuilder::appendBytesRef); BytesRefVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); + releaseAndAssertBreaker(vector.asBlock()); } public void testBytesRefBlockBuilderWithNulls() { int positionCount = randomIntBetween(0, 16 * 1024); final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - var blockBuilder = BytesRefBlock.newBlockBuilder(builderEstimateSize); + var blockBuilder = blockFactory.newBytesRefBlockBuilder(builderEstimateSize); BytesRef[] values = new BytesRef[positionCount]; for (int i = 0; i < positionCount; i++) { if (randomBoolean()) { @@ -451,6 +452,7 @@ public void testBytesRefBlockBuilderWithNulls() { assertThat(block.getBytesRef(pos, bytes), equalTo(values[pos])); } } + releaseAndAssertBreaker(block); } public void testConstantBytesRefBlock() { @@ -459,9 +461,9 @@ public void testConstantBytesRefBlock() { BytesRef value = new BytesRef(randomByteArrayOfLength(between(1, 20))); BytesRefBlock block; if (randomBoolean()) { - block = BytesRefBlock.newConstantBlockWith(value, positionCount); + block = BytesRefBlock.newConstantBlockWith(value, positionCount, blockFactory); } else { - block = new ConstantBytesRefVector(value, positionCount).asBlock(); + block = blockFactory.newConstantBytesRefBlockWith(value, positionCount); } assertThat(block.getPositionCount(), is(positionCount)); @@ -473,6 +475,7 @@ public void testConstantBytesRefBlock() { bytes = block.getBytesRef(randomPosition(positionCount), bytes); assertThat(bytes, is(value)); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); } } @@ -482,7 +485,7 @@ public void testBooleanBlock() { BooleanBlock block; if (randomBoolean()) { final int builderEstimateSize = randomBoolean() ? randomIntBetween(1, positionCount) : positionCount; - var blockBuilder = BooleanBlock.newBlockBuilder(builderEstimateSize); + var blockBuilder = blockFactory.newBooleanBlockBuilder(builderEstimateSize); IntStream.range(0, positionCount).forEach(p -> blockBuilder.appendBoolean(p % 10 == 0)); block = blockBuilder.build(); } else { @@ -490,7 +493,7 @@ public void testBooleanBlock() { for (int p = 0; p < positionCount; p++) { values[p] = p % 10 == 0; } - block = new BooleanArrayVector(values, positionCount).asBlock(); + block = blockFactory.newBooleanArrayVector(values, positionCount).asBlock(); } assertThat(block.getPositionCount(), is(positionCount)); @@ -498,6 +501,11 @@ public void testBooleanBlock() { assertThat(block.getBoolean(positionCount - 1), is((positionCount - 1) % 10 == 0)); assertSingleValueDenseBlock(block); + BooleanBlock.Builder blockBuilder = BooleanBlock.newBlockBuilder(1); + BooleanBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); + assertThat(copy, equalTo(block)); + releaseAndAssertBreaker(block, copy); + if (positionCount > 1) { assertNullValues( positionCount, @@ -511,16 +519,13 @@ public void testBooleanBlock() { ); } - BooleanBlock.Builder blockBuilder = BooleanBlock.newBlockBuilder(1); - BooleanBlock copy = blockBuilder.copyFrom(block, 0, block.getPositionCount()).build(); - assertThat(copy, equalTo(block)); - - BooleanVector.Builder vectorBuilder = BooleanVector.newVectorBuilder( + BooleanVector.Builder vectorBuilder = blockFactory.newBooleanVectorBuilder( randomBoolean() ? randomIntBetween(1, positionCount) : positionCount ); IntStream.range(0, positionCount).mapToObj(ii -> randomBoolean()).forEach(vectorBuilder::appendBoolean); BooleanVector vector = vectorBuilder.build(); assertSingleValueDenseBlock(vector.asBlock()); + releaseAndAssertBreaker(vector.asBlock()); } } @@ -530,15 +535,16 @@ public void testConstantBooleanBlock() { boolean value = randomBoolean(); BooleanBlock block; if (randomBoolean()) { - block = BooleanBlock.newConstantBlockWith(value, positionCount); + block = BooleanBlock.newConstantBlockWith(value, positionCount, blockFactory); } else { - block = new ConstantBooleanVector(value, positionCount).asBlock(); + block = blockFactory.newConstantBooleanBlockWith(value, positionCount); } assertThat(positionCount, is(block.getPositionCount())); assertThat(block.getBoolean(0), is(value)); assertThat(block.getBoolean(positionCount - 1), is(value)); assertThat(block.getBoolean(randomPosition(positionCount)), is(value)); assertSingleValueDenseBlock(block); + releaseAndAssertBreaker(block); } } @@ -856,7 +862,7 @@ interface ValueSupplier { T getValue(int position); } - private static void assertNullValues( + void assertNullValues( int positionCount, BlockBuilderFactory blockBuilderFactory, ValueAppender valueAppender, @@ -885,7 +891,7 @@ private static void assertNullVal asserter.accept(randomNonNullPosition, block); assertTrue(block.isNull(randomNullPosition)); assertFalse(block.isNull(randomNonNullPosition)); - releaseAndAssertBreaker(block, block.blockFactory().breaker()); + releaseAndAssertBreaker(block); } void assertZeroPositionsAndRelease(Block block) { @@ -898,16 +904,36 @@ void assertZeroPositionsAndRelease(Vector vector) { releaseAndAssertBreaker(vector); } - void releaseAndAssertBreaker(T data) { - releaseAndAssertBreaker(data, breaker); + // void releaseAndAssertBreaker(T data) { + // releaseAndAssertBreaker(data, breaker); + // } + + void releaseAndAssertBreaker(Block... blocks) { + assertThat(breaker.getUsed(), greaterThan(0L)); + Releasables.closeExpectNoException(blocks); + Arrays.stream(blocks).forEach(block -> assertThat(block.isReleased(), is(true))); + Arrays.stream(blocks).forEach(BasicBlockTests::assertCannotDoubleRelease); + Arrays.stream(blocks).forEach(BasicBlockTests::assertCannotReadFromPage); + assertThat(breaker.getUsed(), is(0L)); } - static void releaseAndAssertBreaker(T data, CircuitBreaker breaker) { + void releaseAndAssertBreaker(Vector vector) { assertThat(breaker.getUsed(), greaterThan(0L)); - Releasables.closeExpectNoException(data); + Releasables.closeExpectNoException(vector); assertThat(breaker.getUsed(), is(0L)); } + static void assertCannotDoubleRelease(Block block) { + var ex = expectThrows(IllegalStateException.class, () -> block.close()); + assertThat(ex.getMessage(), containsString("can't release already released block")); + } + + static void assertCannotReadFromPage(Block block) { + Page page = new Page(block); + var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); + assertThat(e.getMessage(), containsString("can't read released block")); + } + static int randomPosition(int positionCount) { return positionCount == 1 ? 0 : randomIntBetween(0, positionCount - 1); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java index 831be03cb0c81..f6ecdcec9c8df 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.function.Supplier; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; @@ -552,6 +553,12 @@ static Block.MvOrdering randomOrdering() { void releaseAndAssertBreaker(T data) { assertThat(breaker.getUsed(), greaterThan(0L)); Releasables.closeExpectNoException(data); + if (data instanceof Block block) { + assertThat(block.isReleased(), is(true)); + Page page = new Page(block); + var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); + assertThat(e.getMessage(), containsString("can't read released block")); + } assertThat(breaker.getUsed(), is(0L)); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java index 85e578fcbd38f..d8258ab28a078 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.data; import org.elasticsearch.common.Randomness; +import org.elasticsearch.core.Releasables; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; @@ -15,7 +16,9 @@ import java.util.Comparator; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; public class DocVectorTests extends ESTestCase { public void testNonDecreasingSetTrue() { @@ -123,4 +126,19 @@ private void assertShardSegmentDocMap(int[][] data, int[][] expected) { assertThat(result, equalTo(data)); } + + public void testCannotDoubleRelease() { + var block = new DocVector(IntVector.range(0, 2), IntBlock.newConstantBlockWith(0, 2).asVector(), IntVector.range(0, 2), null) + .asBlock(); + assertThat(block.isReleased(), is(false)); + Releasables.closeExpectNoException(block); + assertThat(block.isReleased(), is(true)); + + var ex = expectThrows(IllegalStateException.class, () -> block.close()); + assertThat(ex.getMessage(), containsString("can't release already released block")); + + Page page = new Page(block); + var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); + assertThat(e.getMessage(), containsString("can't read released block")); + } } From 11f9e57b5bfb3ab115d21c02e107d25e1eb449f7 Mon Sep 17 00:00:00 2001 From: ChrisHegarty Date: Wed, 27 Sep 2023 15:17:01 +0100 Subject: [PATCH 096/155] Remove leftover in test - stray review comment --- .../java/org/elasticsearch/compute/data/BasicBlockTests.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index b6923926f3780..cf7fbbea1c775 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -904,10 +904,6 @@ void assertZeroPositionsAndRelease(Vector vector) { releaseAndAssertBreaker(vector); } - // void releaseAndAssertBreaker(T data) { - // releaseAndAssertBreaker(data, breaker); - // } - void releaseAndAssertBreaker(Block... blocks) { assertThat(breaker.getUsed(), greaterThan(0L)); Releasables.closeExpectNoException(blocks); From 05aec71017f032bc9aff6c1865667e90ac650b41 Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Wed, 27 Sep 2023 16:20:25 +0200 Subject: [PATCH 097/155] [Profiling] Allow to customize the ILM policy (#99909) With this commit we reference the component template `profiling-ilm@custom` in all composable index templates that define indices / data streams managed by ILM. This allows users to override the built-in ILM policy and define e.g. a shorter retention period. --------- Co-authored-by: Christos Kalkanis --- docs/changelog/99909.yaml | 5 +++++ .../resources/profiling/index-template/profiling-events.json | 4 +++- .../profiling/index-template/profiling-executables.json | 4 +++- .../resources/profiling/index-template/profiling-hosts.json | 4 +++- .../profiling/index-template/profiling-metrics.json | 4 +++- .../profiling/index-template/profiling-stackframes.json | 4 +++- .../profiling/index-template/profiling-stacktraces.json | 4 +++- .../profiling/index-template/profiling-symbols-global.json | 4 +++- .../xpack/profiling/ProfilingIndexTemplateRegistry.java | 3 ++- 9 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 docs/changelog/99909.yaml diff --git a/docs/changelog/99909.yaml b/docs/changelog/99909.yaml new file mode 100644 index 0000000000000..2051a30e4efa1 --- /dev/null +++ b/docs/changelog/99909.yaml @@ -0,0 +1,5 @@ +pr: 99909 +summary: "[Profiling] Allow to customize the ILM policy" +area: Application +type: enhancement +issues: [] diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-events.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-events.json index 2ade70d6a0a81..795e2d318ddb9 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-events.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-events.json @@ -7,8 +7,10 @@ }, "composed_of": [ "profiling-events", - "profiling-ilm" + "profiling-ilm", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Index template for profiling-events" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-executables.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-executables.json index 8642d23cd446c..41df385022fdf 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-executables.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-executables.json @@ -5,8 +5,10 @@ "composed_of": [ "profiling-executables", "profiling-ilm", - "profiling-hot-tier" + "profiling-hot-tier", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Index template for .profiling-executables" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-hosts.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-hosts.json index ca8409dab4129..6189c86bb2999 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-hosts.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-hosts.json @@ -5,8 +5,10 @@ "data_stream": {}, "composed_of": [ "profiling-hosts", - "profiling-ilm" + "profiling-ilm", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Template for profiling-hosts" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-metrics.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-metrics.json index 080bd7d5bcef4..b9154bfa54334 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-metrics.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-metrics.json @@ -5,8 +5,10 @@ "data_stream": {}, "composed_of": [ "profiling-metrics", - "profiling-ilm" + "profiling-ilm", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Template for profiling-metrics" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stackframes.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stackframes.json index 76825888dbae5..eed570de0a608 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stackframes.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stackframes.json @@ -5,8 +5,10 @@ "composed_of": [ "profiling-stackframes", "profiling-ilm", - "profiling-hot-tier" + "profiling-hot-tier", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Index template for .profiling-stackframes" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stacktraces.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stacktraces.json index eef41ee8384ad..3797b87b17f10 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stacktraces.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-stacktraces.json @@ -5,8 +5,10 @@ "composed_of": [ "profiling-stacktraces", "profiling-ilm", - "profiling-hot-tier" + "profiling-hot-tier", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "priority": 100, "_meta": { "description": "Index template for .profiling-stacktraces" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-symbols-global.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-symbols-global.json index 5ac22569f9adc..cd6c597f19689 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-symbols-global.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-symbols-global.json @@ -5,8 +5,10 @@ "composed_of": [ "profiling-symbols", "profiling-ilm", - "profiling-hot-tier" + "profiling-hot-tier", + "profiling-ilm@custom" ], + "ignore_missing_component_templates": ["profiling-ilm@custom"], "template": { "settings": { "index": { diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java index 1d19210c31692..36dc280fc1a74 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java @@ -41,7 +41,8 @@ public class ProfilingIndexTemplateRegistry extends IndexTemplateRegistry { // history (please add a comment why you increased the version here) // version 1: initial // version 2: Added 'profiling.host.machine' keyword mapping to profiling-hosts - public static final int INDEX_TEMPLATE_VERSION = 2; + // version 3: Add optional component template 'profiling-ilm@custom' to all ILM-managed index templates + public static final int INDEX_TEMPLATE_VERSION = 3; // history for individual indices / index templates. Only bump these for breaking changes that require to create a new index public static final int PROFILING_EVENTS_VERSION = 1; From a395391220b92042d0db531d9f67b80015b6509d Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Wed, 27 Sep 2023 09:37:11 -0500 Subject: [PATCH 098/155] Updating data streams rest tests to use a replica (#99835) --- .../datastreams/TsdbDataStreamRestIT.java | 10 ++++----- .../DataStreamsClientYamlTestSuiteIT.java | 22 +++++++++++++------ .../test/data_stream/10_basic.yml | 10 ++++++++- .../data_stream/140_data_stream_aliases.yml | 4 ++-- .../test/data_stream/150_tsdb.yml | 6 ++++- .../30_auto_create_data_stream.yml | 2 +- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/TsdbDataStreamRestIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/TsdbDataStreamRestIT.java index 1dba78b8cc431..e8fb6c8b064c4 100644 --- a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/TsdbDataStreamRestIT.java +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/TsdbDataStreamRestIT.java @@ -50,7 +50,7 @@ public class TsdbDataStreamRestIT extends DisabledSecurityDataStreamTestCase { "template": { "settings":{ "index": { - "number_of_replicas": 0, + "number_of_replicas": 1, "number_of_shards": 2, "mode": "time_series" } @@ -117,7 +117,7 @@ public class TsdbDataStreamRestIT extends DisabledSecurityDataStreamTestCase { "template": { "settings":{ "index": { - "number_of_replicas": 0, + "number_of_replicas": 1, "number_of_shards": 2 } }, @@ -408,7 +408,7 @@ public void testSimulateTsdbDataStreamTemplate() throws Exception { var responseBody = entityAsMap(response); assertThat(ObjectPath.evaluate(responseBody, "template.settings.index"), aMapWithSize(6)); assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.number_of_shards"), equalTo("2")); - assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.number_of_replicas"), equalTo("0")); + assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.number_of_replicas"), equalTo("1")); assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.mode"), equalTo("time_series")); assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.time_series.start_time"), notNullValue()); assertThat(ObjectPath.evaluate(responseBody, "template.settings.index.time_series.end_time"), notNullValue()); @@ -629,7 +629,7 @@ public void testLookBackTime() throws IOException { "settings":{ "index": { "look_back_time": "24h", - "number_of_replicas": 0, + "number_of_replicas": 1, "mode": "time_series" } }, @@ -695,7 +695,7 @@ public void testReindexTsdbDataStream() throws Exception { "template": { "settings":{ "index": { - "number_of_replicas": 0, + "number_of_replicas": 1, "number_of_shards": 4, "mode": "time_series", "routing_path": ["metricset", "k8s.pod.uid"], diff --git a/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java b/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java index 3089b778c69c6..fa7b4ca1a80c0 100644 --- a/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java +++ b/modules/data-streams/src/yamlRestTest/java/org/elasticsearch/datastreams/DataStreamsClientYamlTestSuiteIT.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.test.cluster.ElasticsearchCluster; +import org.elasticsearch.test.cluster.local.LocalClusterSpecBuilder; import org.elasticsearch.test.cluster.local.distribution.DistributionType; import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; @@ -37,13 +38,20 @@ protected Settings restClientSettings() { } @ClassRule - public static ElasticsearchCluster cluster = ElasticsearchCluster.local() - .distribution(DistributionType.DEFAULT) - .module("reindex") - .setting("xpack.security.enabled", "true") - .keystore("bootstrap.password", "x-pack-test-password") - .user("x_pack_rest_user", "x-pack-test-password") - .build(); + public static ElasticsearchCluster cluster = createCluster(); + + private static ElasticsearchCluster createCluster() { + LocalClusterSpecBuilder clusterBuilder = ElasticsearchCluster.local() + .distribution(DistributionType.DEFAULT) + .setting("xpack.security.enabled", "true") + .keystore("bootstrap.password", "x-pack-test-password") + .user("x_pack_rest_user", "x-pack-test-password"); + boolean setNodes = Boolean.parseBoolean(System.getProperty("yaml.rest.tests.set_num_nodes", "true")); + if (setNodes) { + clusterBuilder.nodes(2); + } + return clusterBuilder.build(); + } @Override protected String getTestRestCluster() { diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml index 0c2279608c316..50c8e2c74dc74 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml @@ -10,7 +10,7 @@ setup: index_patterns: [simple-data-stream1] template: settings: - index.number_of_replicas: 0 + index.number_of_replicas: 1 data_stream: {} - do: @@ -43,6 +43,10 @@ setup: name: simple-data-stream2 - is_true: acknowledged + - do: + cluster.health: + wait_for_status: green + - do: indices.get_data_stream: name: "*" @@ -637,6 +641,10 @@ setup: name: simple-data-stream2 - is_true: acknowledged + - do: + cluster.health: + wait_for_status: green + - do: indices.get_data_stream: name: "*" diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml index b435fbcebbc24..f036ab549a94f 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml @@ -14,7 +14,7 @@ index_patterns: [events-*] template: settings: - index.number_of_replicas: 0 + index.number_of_replicas: 1 data_stream: {} - do: @@ -88,7 +88,7 @@ index_patterns: [ log-* ] template: settings: - index.number_of_replicas: 0 + index.number_of_replicas: 1 data_stream: { } - do: diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/150_tsdb.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/150_tsdb.yml index b1b6cfa772bcc..6a84f08a2c193 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/150_tsdb.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/150_tsdb.yml @@ -16,7 +16,7 @@ setup: settings: index: mode: time_series - number_of_replicas: 0 + number_of_replicas: 1 number_of_shards: 2 routing_path: [metricset, time_series_dimension] time_series: @@ -90,6 +90,10 @@ created the data stream: version: " - 8.0.99" reason: introduced in 8.1.0 + - do: + cluster.health: + wait_for_status: green + - do: indices.get_data_stream: name: '*' diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/30_auto_create_data_stream.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/30_auto_create_data_stream.yml index eabfb398f3631..2a6beb4330e68 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/30_auto_create_data_stream.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/30_auto_create_data_stream.yml @@ -16,7 +16,7 @@ template: settings: number_of_shards: 1 - number_of_replicas: 0 + number_of_replicas: 1 - do: index: From cb786acf294b4b444ffcc3ffb6ffb9d4361daa2e Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Wed, 27 Sep 2023 16:42:19 +0200 Subject: [PATCH 099/155] [Transform] improve transform log/audit correctness when stopped (#99917) move auditor and logging for transform being stopped, so it gets reported after the p-task has been marked for completion --- docs/changelog/99917.yaml | 5 +++++ .../transforms/ClientTransformIndexer.java | 1 + .../transform/transforms/TransformIndexer.java | 17 ++++++++--------- 3 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/99917.yaml diff --git a/docs/changelog/99917.yaml b/docs/changelog/99917.yaml new file mode 100644 index 0000000000000..6fe77926679f8 --- /dev/null +++ b/docs/changelog/99917.yaml @@ -0,0 +1,5 @@ +pr: 99917 +summary: Improve transform log/audit correctness when stopped +area: Transform +type: enhancement +issues: [] diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java index 00fa7f200a3c3..27dce82b64c3a 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java @@ -395,6 +395,7 @@ protected void afterFinishOrFailure() { @Override protected void onStop() { + logger.debug(() -> format("[%s] transform initiating stop", transformConfig.getId())); closePointInTime(); super.onStop(); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java index 9294aef87526d..6296bdf1277ff 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java @@ -564,7 +564,10 @@ private void finalizeCheckpoint(ActionListener listener) { @Override protected void afterFinishOrFailure() { - finishIndexerThreadShutdown(); + finishIndexerThreadShutdown(() -> { + auditor.info(transformConfig.getId(), "Transform has stopped."); + logger.info("[{}] transform has stopped.", transformConfig.getId()); + }); } @Override @@ -646,12 +649,6 @@ protected void onFailure(Exception exc) { } } - @Override - protected void onStop() { - auditor.info(transformConfig.getId(), "Transform has stopped."); - logger.info("[{}] transform has stopped.", transformConfig.getId()); - } - @Override protected void onAbort() { auditor.info(transformConfig.getId(), "Received abort request, stopping transform."); @@ -1203,7 +1200,7 @@ private void startIndexerThreadShutdown() { } } - private void finishIndexerThreadShutdown() { + private void finishIndexerThreadShutdown(Runnable next) { synchronized (context) { indexerThreadShuttingDown = false; if (saveStateRequestedDuringIndexerThreadShutdown) { @@ -1212,7 +1209,9 @@ private void finishIndexerThreadShutdown() { if (context.shouldStopAtCheckpoint() && nextCheckpoint == null) { stop(); } - doSaveState(getState(), getPosition(), () -> {}); + doSaveState(getState(), getPosition(), next); + } else { + next.run(); } } } From ae743e673d631bf78a2b176891fde2bc560c7c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Fern=C3=A1ndez=20Casta=C3=B1o?= Date: Wed, 27 Sep 2023 16:51:04 +0200 Subject: [PATCH 100/155] Skip settings validation during desired nodes updates (#99946) This commit skips settings validation during desired nodes updates. The issue comes when a setting that needs to be validated depends on a secure setting that cannot be read while the desired nodes are updated. To avoid such issues, we'll skip the settings validations completely. Closes #96127 --- docs/changelog/99946.yaml | 5 + .../test/cluster.desired_nodes/10_basic.yml | 34 ++++ .../test/cluster.desired_nodes/20_dry_run.yml | 3 + .../TransportDesiredNodesActionsIT.java | 157 ++++-------------- .../DesiredNodesSettingsValidator.java | 74 ++------- .../java/org/elasticsearch/node/Node.java | 4 +- ...ransportUpdateDesiredNodesActionTests.java | 38 +---- .../DesiredNodesSettingsValidatorTests.java | 107 +----------- 8 files changed, 95 insertions(+), 327 deletions(-) create mode 100644 docs/changelog/99946.yaml diff --git a/docs/changelog/99946.yaml b/docs/changelog/99946.yaml new file mode 100644 index 0000000000000..11dc4090baa0e --- /dev/null +++ b/docs/changelog/99946.yaml @@ -0,0 +1,5 @@ +pr: 99946 +summary: Skip settings validation during desired nodes updates +area: Distributed +type: bug +issues: [] diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/10_basic.yml index c7757d74d54ae..28b79597cb2da 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/10_basic.yml @@ -329,6 +329,9 @@ teardown: - { settings: { node: { external_id: "instance-000187" } }, processors: 8.0, memory: "64gb", storage: "128gb", node_version: $es_version } --- "Test settings are validated": + - skip: + version: "8.9.99 - " + reason: "We started skipping setting validations in 8.10" - do: cluster.state: {} @@ -352,6 +355,9 @@ teardown: - match: { error.suppressed.0.reason: "Failed to parse value [-1000] for setting [http.tcp.keep_idle] must be >= -1" } --- "Test unknown settings are forbidden in known versions": + - skip: + version: "8.9.99 - " + reason: "We started skipping setting validations in 8.10" - do: cluster.state: {} @@ -375,6 +381,9 @@ teardown: - match: { error.suppressed.0.reason: "unknown setting [unknown_setting] please check that any required plugins are installed, or check the breaking changes documentation for removed settings" } --- "Test unknown settings are allowed in future versions": + - skip: + version: "8.9.99 - " + reason: "We started skipping setting validations in 8.10" - do: _internal.update_desired_nodes: history_id: "test" @@ -385,6 +394,9 @@ teardown: - match: { replaced_existing_history_id: false } --- "Test some settings can be overridden": + - skip: + version: "8.9.99 - " + reason: "We started skipping setting validations in 8.10" - do: cluster.state: {} @@ -1154,3 +1166,25 @@ teardown: - { settings: { "node.name": "instance-000187" }, processors: 1.0, processors_range: {min: 1.0, max: 2.0}, memory: "64gb", storage: "128gb", node_version: $es_version } - match: { status: 400 } - match: { error.type: x_content_parse_exception } +--- +"Test node roles are validated": + - do: + cluster.state: {} + + # Get master node id + - set: { master_node: master } + + - do: + nodes.info: {} + - set: { nodes.$master.version: es_version } + + - do: + catch: bad_request + _internal.update_desired_nodes: + history_id: "test" + version: 1 + body: + nodes: + - { settings: { "node.name": "instance-000187", "node.roles": "unknown,other" }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version } + - match: { status: 400 } + - match: { error.type: x_content_parse_exception } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/20_dry_run.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/20_dry_run.yml index 774fb23f9754b..339c0cef3e162 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/20_dry_run.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_nodes/20_dry_run.yml @@ -87,6 +87,9 @@ teardown: - { settings: { node: { name: "instance-000187" } }, processors: 8.5, memory: "64gb", storage: "128gb", node_version: $es_version } --- "Test validation works for dry run updates": + - skip: + version: "8.9.99 - " + reason: "We started skipping setting validations in 8.10" - do: cluster.state: { } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDesiredNodesActionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDesiredNodesActionsIT.java index 1ed712de85b7d..76d456bae1c06 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDesiredNodesActionsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportDesiredNodesActionsIT.java @@ -29,13 +29,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.elasticsearch.cluster.metadata.DesiredNodesTestCase.randomDesiredNode; import static org.elasticsearch.common.util.concurrent.EsExecutors.NODE_PROCESSORS_SETTING; -import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_TCP_KEEP_IDLE; import static org.elasticsearch.node.NodeRoleSettings.NODE_ROLES_SETTING; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -83,19 +81,6 @@ public void testDryRunUpdateDoesNotUpdateExistingDesiredNodes() { assertEquals(getLatestDesiredNodes(), desiredNodes); } - public void testSettingsAreValidatedWithDryRun() { - var exception = expectThrows( - IllegalArgumentException.class, - () -> updateDesiredNodes( - randomDryRunUpdateDesiredNodesRequest( - Version.CURRENT, - Settings.builder().put(SETTING_HTTP_TCP_KEEP_IDLE.getKey(), Integer.MIN_VALUE).build() - ) - ) - ); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - } - public void testUpdateDesiredNodesIsIdempotent() { final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest(); updateDesiredNodes(updateDesiredNodesRequest); @@ -195,52 +180,6 @@ public void testAtLeastOneMaterNodeIsExpected() { } } - public void testSettingsAreValidated() { - final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest( - Settings.builder().put(SETTING_HTTP_TCP_KEEP_IDLE.getKey(), Integer.MIN_VALUE).build() - ); - - final IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> updateDesiredNodes(updateDesiredNodesRequest) - ); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat( - exception.getSuppressed()[0].getMessage(), - containsString("Failed to parse value [-2147483648] for setting [http.tcp.keep_idle] must be >= -1") - ); - } - - public void testNodeVersionIsValidated() { - final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest(Version.CURRENT.previousMajor(), Settings.EMPTY); - - final IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> updateDesiredNodes(updateDesiredNodesRequest) - ); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat(exception.getSuppressed()[0].getMessage(), containsString("Illegal node version")); - } - - public void testUnknownSettingsAreForbiddenInKnownVersions() { - final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest( - Settings.builder().put("desired_nodes.random_setting", Integer.MIN_VALUE).build() - ); - - final IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> updateDesiredNodes(updateDesiredNodesRequest) - ); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat(exception.getSuppressed()[0].getMessage(), containsString("unknown setting [desired_nodes.random_setting]")); - } - public void testUnknownSettingsAreAllowedInFutureVersions() { final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest( Version.fromString("99.9.0"), @@ -255,74 +194,48 @@ public void testUnknownSettingsAreAllowedInFutureVersions() { public void testNodeProcessorsGetValidatedWithDesiredNodeProcessors() { final int numProcessors = Math.max(Runtime.getRuntime().availableProcessors() + 1, 2048); - - { - final var updateDesiredNodesRequest = new UpdateDesiredNodesRequest( - UUIDs.randomBase64UUID(), - randomIntBetween(1, 20), - randomList( - 1, - 20, - () -> randomDesiredNode( - Version.CURRENT, - Settings.builder().put(NODE_PROCESSORS_SETTING.getKey(), numProcessors + 1).build(), - numProcessors - ) - ), - false - ); - - final IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> updateDesiredNodes(updateDesiredNodesRequest) - ); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat( - exception.getSuppressed()[0].getMessage(), - containsString( - String.format( - Locale.ROOT, - "Failed to parse value [%d] for setting [node.processors] must be <= %d", - numProcessors + 1, - numProcessors - ) + // This test verifies that the validation doesn't throw on desired nodes + // with a higher number of available processors than the node running the tests. + final var updateDesiredNodesRequest = new UpdateDesiredNodesRequest( + UUIDs.randomBase64UUID(), + randomIntBetween(1, 20), + randomList( + 1, + 20, + () -> randomDesiredNode( + Version.CURRENT, + Settings.builder().put(NODE_PROCESSORS_SETTING.getKey(), numProcessors).build(), + numProcessors ) - ); - } - - { - // This test verifies that the validation doesn't throw on desired nodes - // with a higher number of available processors than the node running the tests. - final var updateDesiredNodesRequest = new UpdateDesiredNodesRequest( - UUIDs.randomBase64UUID(), - randomIntBetween(1, 20), - randomList( - 1, - 20, - () -> randomDesiredNode( - Version.CURRENT, - Settings.builder().put(NODE_PROCESSORS_SETTING.getKey(), numProcessors).build(), - numProcessors - ) - ), - false - ); + ), + false + ); - updateDesiredNodes(updateDesiredNodesRequest); + updateDesiredNodes(updateDesiredNodesRequest); - final DesiredNodes latestDesiredNodes = getLatestDesiredNodes(); - assertStoredDesiredNodesAreCorrect(updateDesiredNodesRequest, latestDesiredNodes); + final DesiredNodes latestDesiredNodes = getLatestDesiredNodes(); + assertStoredDesiredNodesAreCorrect(updateDesiredNodesRequest, latestDesiredNodes); - assertThat(latestDesiredNodes.nodes().isEmpty(), is(equalTo(false))); - for (final var desiredNodeWithStatus : latestDesiredNodes) { - final var desiredNode = desiredNodeWithStatus.desiredNode(); - assertThat(desiredNode.settings().get(NODE_PROCESSORS_SETTING.getKey()), is(equalTo(Integer.toString(numProcessors)))); - } + assertThat(latestDesiredNodes.nodes().isEmpty(), is(equalTo(false))); + for (final var desiredNodeWithStatus : latestDesiredNodes) { + final var desiredNode = desiredNodeWithStatus.desiredNode(); + assertThat(desiredNode.settings().get(NODE_PROCESSORS_SETTING.getKey()), is(equalTo(Integer.toString(numProcessors)))); } } + public void testNodeVersionIsValidated() { + final var updateDesiredNodesRequest = randomUpdateDesiredNodesRequest(Version.CURRENT.previousMajor(), Settings.EMPTY); + + final IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> updateDesiredNodes(updateDesiredNodesRequest) + ); + assertThat(exception.getMessage(), containsString("Nodes with ids")); + assertThat(exception.getMessage(), containsString("contain invalid settings")); + assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); + assertThat(exception.getSuppressed()[0].getMessage(), containsString("Illegal node version")); + } + public void testUpdateDesiredNodesTasksAreBatchedCorrectly() throws Exception { final Runnable unblockClusterStateUpdateThread = blockClusterStateUpdateThread(); diff --git a/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java b/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java index cd1298e4c6ef7..51f90b9610805 100644 --- a/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java +++ b/server/src/main/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidator.java @@ -11,9 +11,6 @@ import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.DesiredNode; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; import java.util.ArrayList; @@ -22,25 +19,29 @@ import java.util.stream.Collectors; import static java.lang.String.format; -import static org.elasticsearch.common.util.concurrent.EsExecutors.NODE_PROCESSORS_SETTING; public class DesiredNodesSettingsValidator { private record DesiredNodeValidationError(int position, @Nullable String externalId, RuntimeException exception) {} - private final ClusterSettings clusterSettings; - - public DesiredNodesSettingsValidator(ClusterSettings clusterSettings) { - this.clusterSettings = clusterSettings; - } - public void validate(List nodes) { final List validationErrors = new ArrayList<>(); for (int i = 0; i < nodes.size(); i++) { final DesiredNode node = nodes.get(i); - try { - validate(node); - } catch (IllegalArgumentException e) { - validationErrors.add(new DesiredNodeValidationError(i, node.externalId(), e)); + if (node.version().before(Version.CURRENT)) { + validationErrors.add( + new DesiredNodeValidationError( + i, + node.externalId(), + new IllegalArgumentException( + format( + Locale.ROOT, + "Illegal node version [%s]. Only [%s] or newer versions are supported", + node.version(), + Build.current().version() + ) + ) + ) + ); } } @@ -68,49 +69,4 @@ public void validate(List nodes) { } } - private void validate(DesiredNode node) { - if (node.version().before(Version.CURRENT)) { - throw new IllegalArgumentException( - format( - Locale.ROOT, - "Illegal node version [%s]. Only [%s] or newer versions are supported", - node.version(), - Build.current().version() - ) - ); - } - - // Validating settings for future versions can be unsafe: - // - If the legal range is upgraded in the newer version - // - If a new setting is used as the default value for a previous setting - // To avoid considering these as invalid settings, - // We just don't validate settings for versions in newer versions. - if (node.version().after(Version.CURRENT)) { - return; - } - - Settings settings = node.settings(); - - // node.processors rely on the environment to define its ranges, in this case - // we create a new setting just to run the validations using the desired node - // number of available processors - if (settings.hasValue(NODE_PROCESSORS_SETTING.getKey())) { - int minProcessors = node.roundedDownMinProcessors(); - Integer roundedUpMaxProcessors = node.roundedUpMaxProcessors(); - int maxProcessors = roundedUpMaxProcessors == null ? minProcessors : roundedUpMaxProcessors; - Setting.doubleSetting( - NODE_PROCESSORS_SETTING.getKey(), - minProcessors, - Double.MIN_VALUE, - maxProcessors, - Setting.Property.NodeScope - ).get(settings); - final Settings.Builder updatedSettings = Settings.builder().put(settings); - updatedSettings.remove(NODE_PROCESSORS_SETTING.getKey()); - settings = updatedSettings.build(); - } - - clusterSettings.validate(settings, true); - } - } diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index a2da65bdaa8b4..59f914d27382d 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -1043,9 +1043,7 @@ protected Node( clusterService.addListener(pluginShutdownService); final RecoveryPlannerService recoveryPlannerService = getRecoveryPlannerService(threadPool, clusterService, repositoryService); - final DesiredNodesSettingsValidator desiredNodesSettingsValidator = new DesiredNodesSettingsValidator( - clusterService.getClusterSettings() - ); + final DesiredNodesSettingsValidator desiredNodesSettingsValidator = new DesiredNodesSettingsValidator(); final MasterHistoryService masterHistoryService = new MasterHistoryService(transportService, threadPool, clusterService); final CoordinationDiagnosticsService coordinationDiagnosticsService = new CoordinationDiagnosticsService( diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesActionTests.java index 508cb9b304c59..b2c2b49c90eb2 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/desirednodes/TransportUpdateDesiredNodesActionTests.java @@ -9,7 +9,6 @@ package org.elasticsearch.action.admin.cluster.desirednodes; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlockException; @@ -26,7 +25,6 @@ import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.tasks.Task; import org.elasticsearch.test.MockUtils; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -42,14 +40,11 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; public class TransportUpdateDesiredNodesActionTests extends DesiredNodesTestCase { - public static final DesiredNodesSettingsValidator NO_OP_SETTINGS_VALIDATOR = new DesiredNodesSettingsValidator(null) { + public static final DesiredNodesSettingsValidator NO_OP_SETTINGS_VALIDATOR = new DesiredNodesSettingsValidator() { @Override public void validate(List desiredNodes) {} }; @@ -100,37 +95,6 @@ public void testNoBlocks() { assertThat(e, is(nullValue())); } - public void testSettingsGetValidated() throws Exception { - DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(null) { - @Override - public void validate(List desiredNodes) { - throw new IllegalArgumentException("Invalid settings"); - } - }; - ClusterService clusterService = mock(ClusterService.class); - - ThreadPool threadPool = mock(ThreadPool.class); - TransportService transportService = MockUtils.setupTransportServiceWithThreadpoolExecutor(threadPool); - final TransportUpdateDesiredNodesAction action = new TransportUpdateDesiredNodesAction( - transportService, - clusterService, - threadPool, - mock(ActionFilters.class), - mock(IndexNameExpressionResolver.class), - validator, - mock(AllocationService.class) - ); - - final ClusterState state = ClusterState.builder(new ClusterName(randomAlphaOfLength(10))).build(); - - final PlainActionFuture future = PlainActionFuture.newFuture(); - action.masterOperation(mock(Task.class), randomUpdateDesiredNodesRequest(), state, future); - IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, future::actionGet); - assertThat(exception.getMessage(), containsString("Invalid settings")); - - verify(clusterService, never()).submitUnbatchedStateUpdateTask(any(), any()); - } - public void testUpdateDesiredNodes() { final Metadata.Builder metadataBuilder = Metadata.builder(); boolean containsDesiredNodes = false; diff --git a/server/src/test/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidatorTests.java b/server/src/test/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidatorTests.java index 5961da02a1272..005252994d77e 100644 --- a/server/src/test/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidatorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/desirednodes/DesiredNodesSettingsValidatorTests.java @@ -10,58 +10,21 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.DesiredNode; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.test.ESTestCase; -import java.util.Collections; import java.util.List; -import java.util.Set; import static org.elasticsearch.cluster.metadata.DesiredNodesTestCase.randomDesiredNode; -import static org.elasticsearch.common.util.concurrent.EsExecutors.NODE_PROCESSORS_SETTING; -import static org.elasticsearch.node.Node.NODE_EXTERNAL_ID_SETTING; -import static org.elasticsearch.node.Node.NODE_NAME_SETTING; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; public class DesiredNodesSettingsValidatorTests extends ESTestCase { - - public void testSettingsValidation() { - final Set> availableSettings = Set.of( - Setting.intSetting("test.invalid_value", 1, Setting.Property.NodeScope), - Setting.intSetting("test.invalid_range", 1, 1, 100, Setting.Property.NodeScope), - NODE_EXTERNAL_ID_SETTING, - NODE_NAME_SETTING - ); - final Settings.Builder settings = Settings.builder(); - - if (randomBoolean()) { - settings.put("test.invalid_value", randomAlphaOfLength(10)); - } else { - settings.put("test.invalid_range", randomFrom(-1, Integer.MAX_VALUE)); - } - - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, availableSettings); - final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(clusterSettings); - - final List desiredNodes = randomList(2, 10, () -> randomDesiredNode(Version.CURRENT, settings.build())); - - IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> validator.validate(desiredNodes)); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat(exception.getSuppressed()[0].getMessage(), containsString("Failed to parse value")); - } - public void testNodeVersionValidation() { final List desiredNodes = List.of(randomDesiredNode(Version.CURRENT.previousMajor(), Settings.EMPTY)); - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, Collections.emptySet()); - final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(clusterSettings); + final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(); final IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> validator.validate(desiredNodes)); assertThat(exception.getMessage(), containsString("Nodes with ids")); @@ -69,72 +32,4 @@ public void testNodeVersionValidation() { assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); assertThat(exception.getSuppressed()[0].getMessage(), containsString("Illegal node version")); } - - public void testUnknownSettingsInKnownVersionsAreInvalid() { - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, Collections.emptySet()); - final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(clusterSettings); - final List desiredNodes = randomList(2, 10, () -> randomDesiredNode(Version.CURRENT, Settings.EMPTY)); - - IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> validator.validate(desiredNodes)); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat(exception.getSuppressed()[0].getMessage(), containsString("unknown setting")); - } - - public void testUnknownSettingsInFutureVersionsAreNotValidated() { - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, Collections.emptySet()); - final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(clusterSettings); - - final List desiredNodes = randomList( - 1, - 10, - () -> randomDesiredNode( - Version.fromString("99.9.0"), - Settings.builder().put(randomAlphaOfLength(10), randomAlphaOfLength(10)).build() - ) - ); - validator.validate(desiredNodes); - } - - public void testNodeProcessorsValidation() { - final Set> availableSettings = Set.of(NODE_PROCESSORS_SETTING, NODE_EXTERNAL_ID_SETTING, NODE_NAME_SETTING); - - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, availableSettings); - final DesiredNodesSettingsValidator validator = new DesiredNodesSettingsValidator(clusterSettings); - - { - int desiredNodeProcessors = 128; - Settings nodeSettings = Settings.builder() - .put(NODE_EXTERNAL_ID_SETTING.getKey(), randomAlphaOfLength(10)) - .put(NODE_PROCESSORS_SETTING.getKey(), desiredNodeProcessors) - .build(); - final List desiredNodes = List.of( - new DesiredNode(nodeSettings, desiredNodeProcessors, ByteSizeValue.ofGb(1), ByteSizeValue.ofGb(1), Version.CURRENT) - ); - - validator.validate(desiredNodes); - } - - { - int desiredNodeProcessors = 128; - Settings nodeSettings = Settings.builder() - .put(NODE_EXTERNAL_ID_SETTING.getKey(), randomAlphaOfLength(10)) - .put(NODE_PROCESSORS_SETTING.getKey(), desiredNodeProcessors + 1.1) - .build(); - final List desiredNodes = List.of( - new DesiredNode(nodeSettings, desiredNodeProcessors, ByteSizeValue.ofGb(1), ByteSizeValue.ofGb(1), Version.CURRENT) - ); - - final IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> validator.validate(desiredNodes)); - assertThat(exception.getMessage(), containsString("Nodes with ids")); - assertThat(exception.getMessage(), containsString("contain invalid settings")); - assertThat(exception.getSuppressed().length > 0, is(equalTo(true))); - assertThat( - exception.getSuppressed()[0].getMessage(), - containsString("Failed to parse value [129.1] for setting [node.processors] must be <= 128.0") - ); - } - - } } From 8b2a9c646be377331f0c6e772bea03ccf3d12ad7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 27 Sep 2023 16:00:22 +0100 Subject: [PATCH 101/155] Add breaking change docs for #92820 (#99849) In #92820 we adjusted the indices resolve API to use the `IndexNameExpressionResolver` to align its behaviour with other similar APIs, but this was a subtle breaking change in its behaviour when there were no matching indices. This adds a note in the docs to record this change in behaviour. --- docs/reference/indices/resolve.asciidoc | 16 ++++++++++++++-- docs/reference/release-notes/8.7.0.asciidoc | 10 ++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/reference/indices/resolve.asciidoc b/docs/reference/indices/resolve.asciidoc index e62e9951fa91c..1f405a2e49a7a 100644 --- a/docs/reference/indices/resolve.asciidoc +++ b/docs/reference/indices/resolve.asciidoc @@ -68,8 +68,8 @@ for the target data stream, index, or index alias. + -- (Required, string) Comma-separated name(s) or index pattern(s) of the -indices, aliases, and data streams to resolve. Resources on -<> can be specified using the +indices, aliases, and data streams to resolve, using <>. +Resources on <> can be specified using the `:` syntax. -- @@ -80,6 +80,18 @@ include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=expand-wildcards] + Defaults to `open`. +include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable] ++ +Defaults to `false`. + +include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=allow-no-indices] ++ +Defaults to `true`. + +include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=ignore_throttled] ++ +Defaults to `false`. + [[resolve-index-api-example]] ==== {api-examples-title} diff --git a/docs/reference/release-notes/8.7.0.asciidoc b/docs/reference/release-notes/8.7.0.asciidoc index d0886a616e341..75cc1d80987aa 100644 --- a/docs/reference/release-notes/8.7.0.asciidoc +++ b/docs/reference/release-notes/8.7.0.asciidoc @@ -16,6 +16,16 @@ include::8.6.0.asciidoc[tag=reconciliation-imbalance-known-issue] Ingest Node:: * Making `JsonProcessor` stricter so that it does not silently drop data {es-pull}93179[#93179] (issue: {es-issue}92898[#92898]) +Indices APIs:: +* The <> API implementation was adjusted to use the +same index resolution mechanism as other similar APIs, adding support for the +`ignore_unavailable` and `allow_no_indices` flags and the `_all` meta-index. If +there are no matching indices then earlier versions of this API would return an +empty result with the `200 OK` HTTP response code, but from 8.7.0 onwards by +default it returns an `IndexNotFoundException` with the `404 Not Found` HTTP +response code. To recover the old behaviour, add the query parameter +`?ignore_unavailable=true` ({es-pull}92820[#92820]). + [[bug-8.7.0]] [float] === Bug fixes From 8be2ab8a5fb6da4b5453550077de05d169b4cb3c Mon Sep 17 00:00:00 2001 From: Salvatore Campagna <93581129+salvatore-campagna@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:24:22 +0200 Subject: [PATCH 102/155] Log TooManyBucketsException (#99955) Here we just log TooManyBucketException to make investigating issues easier. --- .../search/aggregations/MultiBucketConsumerService.java | 4 ++++ .../aggregations/bucket/composite/CompositeAggregator.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java b/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java index 5d29a7104c0b9..fe7ff22ec2b3a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java @@ -13,6 +13,8 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.xcontent.XContentBuilder; @@ -91,6 +93,7 @@ protected void metadataToXContent(XContentBuilder builder, Params params) throws * {@link Aggregator#buildAggregations} and {@link InternalAggregation#reduce}. */ public static class MultiBucketConsumer implements IntConsumer { + private final Logger logger = LogManager.getLogger(MultiBucketConsumer.class); private final int limit; private final CircuitBreaker breaker; @@ -108,6 +111,7 @@ public void accept(int value) { if (value != 0) { count += value; if (count > limit) { + logger.warn("Too many buckets (max [{}], count [{}])", limit, count); throw new TooManyBucketsException( "Trying to create too many buckets. Must be less than or equal to: [" + limit diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java index 3dcc48c8f69d2..1094db38cb14d 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java @@ -36,6 +36,8 @@ import org.elasticsearch.core.Releasables; import org.elasticsearch.core.Strings; import org.elasticsearch.index.IndexSortConfig; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.lucene.queries.SearchAfterSortedDocQuery; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationExecutionContext; @@ -68,6 +70,7 @@ public final class CompositeAggregator extends BucketsAggregator implements SizedBucketAggregator { + private final Logger logger = LogManager.getLogger(CompositeAggregator.class); private final int size; private final List sourceNames; private final int[] reverseMuls; @@ -107,6 +110,7 @@ public final class CompositeAggregator extends BucketsAggregator implements Size // check that the provided size is not greater than the search.max_buckets setting int bucketLimit = aggCtx.maxBuckets(); if (size > bucketLimit) { + logger.warn("Too many buckets (max [{}], count [{}])", bucketLimit, size); throw new MultiBucketConsumerService.TooManyBucketsException( "Trying to create too many buckets. Must be less than or equal" + " to: [" From be2966662fe7db45fb8ce68e03925d413aa2d864 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Wed, 27 Sep 2023 17:45:13 +0200 Subject: [PATCH 103/155] ESQL: Use exact attributes for data source extraction (#99874) --- docs/changelog/99874.yaml | 6 ++++++ .../yamlRestTest/resources/rest-api-spec/test/80_text.yml | 8 ++------ .../xpack/esql/planner/EsPhysicalOperationProviders.java | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 docs/changelog/99874.yaml diff --git a/docs/changelog/99874.yaml b/docs/changelog/99874.yaml new file mode 100644 index 0000000000000..d23fc1ea6edde --- /dev/null +++ b/docs/changelog/99874.yaml @@ -0,0 +1,6 @@ +pr: 99874 +summary: "ESQL: Use exact attributes for data source extraction" +area: ES|QL +type: bug +issues: + - 99183 diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml index 5f9ecbdf747bf..d05bc372326f8 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/80_text.yml @@ -322,8 +322,6 @@ setup: --- "text with synthetic source": - - skip: - features: allowed_warnings_regex - do: indices.create: index: test2 @@ -353,8 +351,6 @@ setup: - { "emp_no": 20, "name": "John", "job": "Payroll Specialist" } - do: - allowed_warnings_regex: - - "Field \\[job\\] cannot be retrieved, it is unsupported or not indexed; returning null" esql.query: body: query: 'from test2 | sort emp_no | keep job' @@ -363,8 +359,8 @@ setup: - match: { columns.0.type: "text" } - length: { values: 2 } - - match: { values.0.0: null } # awaiting a complete fix for https://github.com/elastic/elasticsearch/issues/99183 - - match: { values.1.0: null } # for now, since we cannot extract text values from synthetic source, we at least avoid to throw exceptions + - match: { values.0.0: "IT Director" } + - match: { values.1.0: "Payroll Specialist" } --- diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java index bae1980a3e856..ac62c45d4d1f3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java @@ -36,6 +36,7 @@ import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner.PhysicalOperation; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Attribute; +import org.elasticsearch.xpack.ql.expression.FieldAttribute; import java.util.ArrayList; import java.util.List; @@ -61,6 +62,9 @@ public final PhysicalOperation fieldExtractPhysicalOperation(FieldExtractExec fi PhysicalOperation op = source; for (Attribute attr : fieldExtractExec.attributesToExtract()) { + if (attr instanceof FieldAttribute fa && fa.getExactInfo().hasExact()) { + attr = fa.exactAttribute(); + } layout.append(attr); Layout previousLayout = op.layout; From fe02cf0b2bb2ba9dcab89ee724c4a5481fa20ac4 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 27 Sep 2023 17:37:49 +0100 Subject: [PATCH 104/155] [ML] Switch from Version.CURRENT to Build.current() for index meta (#99942) ML index mappings contain both `managed_index_mappings_version` and `version` meta fields. `managed_index_mappings_version` is used from 8.10 onwards in the mappings management code to determine if the mappings are up to date. However, the `version` field is still required to stop pre-8.10 and post-8.10 nodes fighting and causing repeated mappings updates in mixed-version clusters. The pre-8.10 nodes will replace the mappings if the `version` field is not present, on the assumption that the mappings are _really_ old (5.x or something like that). To reduce use of the `Version.CURRENT` constant this PR changes the `version` field in ML index mappings metadata to be hardcoded as `8.11.0`. Pre-8.10 nodes will see _all_ nodes from 8.11.0 onwards as 8.11.0 for the purposes of determining whether ML index mappings need updating. However, that still works as, from the older node's perspective, it means "newer than I understand". --- .../indices/SystemIndexDescriptor.java | 21 ++- .../xpack/core/ml/MlConfigIndex.java | 4 +- .../xpack/core/ml/MlMetaIndex.java | 4 +- .../xpack/core/ml/MlStatsIndex.java | 3 +- .../core/ml/annotations/AnnotationIndex.java | 4 +- .../persistence/InferenceIndexConstants.java | 4 +- .../persistence/AnomalyDetectorsIndex.java | 3 +- .../ml/notifications/NotificationsIndex.java | 4 +- .../xpack/core/ml/utils/MlIndexAndAlias.java | 12 ++ .../ElasticsearchMappingsTests.java | 157 ++++++++++++------ .../ml/integration/JobResultsProviderIT.java | 5 +- .../upgrades/MlMappingsUpgradeIT.java | 4 - 12 files changed, 152 insertions(+), 73 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java b/server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java index 98cea47a94a5d..fe0b4f418cf96 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java @@ -281,7 +281,7 @@ protected SystemIndexDescriptor( if (settings.getAsInt(IndexMetadata.INDEX_FORMAT_SETTING.getKey(), 0) != indexFormat) { throw new IllegalArgumentException("Descriptor index format does not match index format in managed settings"); } - this.mappingsNodeVersion = extractNodeVersionFromMappings(mappings, mappingsNodeVersionMetaKey); + this.mappingsNodeVersion = bestEffortExtractNodeVersionFromMappings(mappings, mappingsNodeVersionMetaKey); this.mappingsVersion = extractVersionFromMappings(mappings); assert mappingsVersion.version >= 0 : "The mappings version must not be negative"; @@ -953,6 +953,25 @@ private static MappingsVersion extractVersionFromMappings(String mappings) { return new MappingsVersion(value, Objects.hash(properties)); } + /** + * An accurate node version is no longer required in system index mappings metadata. + * because the mappings version should be used to determine if an upgrade is required, + * not the node version. However, some parts of the code are still relying on + * mappingsNodeVersion. This method allows sections of the code to stop + * accurately setting node version in their mappings while other sections continue to + * use it. Once all uses of mappingsNodeVersion are removed this method + * can be removed too. + */ + @Deprecated + private static Version bestEffortExtractNodeVersionFromMappings(String mappings, String versionMetaKey) { + try { + return extractNodeVersionFromMappings(mappings, versionMetaKey); + } catch (Exception e) { + return null; + } + } + + @Deprecated @SuppressWarnings("unchecked") private static Version extractNodeVersionFromMappings(String mappings, String versionMetaKey) { final Map mappingsMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), mappings, false); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigIndex.java index da905ef5efdee..2fdef976e5066 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigIndex.java @@ -6,10 +6,10 @@ */ package org.elasticsearch.xpack.core.ml; -import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.template.TemplateUtils; import java.util.Map; @@ -34,7 +34,7 @@ public static String indexName() { public static String mapping() { return TemplateUtils.loadTemplate( "/ml/config_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(CONFIG_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlMetaIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlMetaIndex.java index 9190be89ee1e0..58978537cd2c2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlMetaIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlMetaIndex.java @@ -6,9 +6,9 @@ */ package org.elasticsearch.xpack.core.ml; -import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.template.TemplateUtils; import java.util.Map; @@ -32,7 +32,7 @@ public static String indexName() { public static String mapping() { return TemplateUtils.loadTemplate( "/ml/meta_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(META_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlStatsIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlStatsIndex.java index 83c7a3fe45a09..97dede7cf0c6f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlStatsIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlStatsIndex.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.core.ml; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterState; @@ -40,7 +39,7 @@ public static String wrappedMapping() { public static String mapping() { return TemplateUtils.loadTemplate( "/ml/stats_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(STATS_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java index 745e50c920288..eb4f4986fa193 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java @@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.ResourceAlreadyExistsException; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; @@ -28,6 +27,7 @@ import org.elasticsearch.xpack.core.ml.MlMetadata; import org.elasticsearch.xpack.core.ml.job.persistence.ElasticsearchMappings; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.template.TemplateUtils; import java.util.List; @@ -213,7 +213,7 @@ public static void createAnnotationsIndexIfNecessary( public static String annotationsMapping() { return TemplateUtils.loadTemplate( "/ml/annotations_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(ANNOTATION_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java index 1e60da895c0e7..ca70f9e9e761d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java @@ -6,10 +6,10 @@ */ package org.elasticsearch.xpack.core.ml.inference.persistence; -import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xcontent.ParseField; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.template.TemplateUtils; import java.util.Map; @@ -51,7 +51,7 @@ public final class InferenceIndexConstants { public static String mapping() { return TemplateUtils.loadTemplate( "/ml/inference_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(INFERENCE_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/AnomalyDetectorsIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/AnomalyDetectorsIndex.java index 29ac3359c870b..37d070d90be76 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/AnomalyDetectorsIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/AnomalyDetectorsIndex.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.core.ml.job.persistence; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; @@ -137,7 +136,7 @@ public static String wrappedResultsMapping() { public static String resultsMapping() { return TemplateUtils.loadTemplate( RESOURCE_PATH + "results_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes RESULTS_MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(RESULTS_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java index 08f493fd29523..1f5c15a46fc4e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java @@ -6,7 +6,7 @@ */ package org.elasticsearch.xpack.core.ml.notifications; -import org.elasticsearch.Version; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.template.TemplateUtils; import java.util.Map; @@ -24,7 +24,7 @@ private NotificationsIndex() {} public static String mapping() { return TemplateUtils.loadTemplate( RESOURCE_PATH + "notifications_index_mappings.json", - Version.CURRENT.toString(), + MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes MAPPINGS_VERSION_VARIABLE, Map.of("xpack.ml.managed.index.version", Integer.toString(NOTIFICATIONS_INDEX_MAPPINGS_VERSION)) ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java index 5351c1b0ff25b..78a3493e8ae6b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java @@ -50,6 +50,18 @@ */ public final class MlIndexAndAlias { + /** + * ML managed index mappings used to be updated based on the product version. + * They are now updated based on per-index mappings versions. However, older + * nodes will still look for a product version in the mappings metadata, so + * we have to put something in that field that will allow the older + * node to realise that the mappings are ahead of what it knows about. The + * easiest solution is to hardcode 8.11.0 in this field, because any node + * from 8.10.0 onwards should be using per-index mappings versions to determine + * whether mappings are up-to-date. + */ + public static final String BWC_MAPPINGS_VERSION = "8.11.0"; + private static final Logger logger = LogManager.getLogger(MlIndexAndAlias.class); // Visible for testing diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java index d95c94801e52a..e1a9b20c048c4 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.core.ml.job.persistence; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; @@ -19,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.MappingMetadata; import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexVersion; @@ -40,6 +40,7 @@ import org.elasticsearch.xpack.core.ml.job.results.CategoryDefinition; import org.elasticsearch.xpack.core.ml.job.results.ReservedFieldNames; import org.elasticsearch.xpack.core.ml.job.results.Result; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.junit.Assert; import org.mockito.ArgumentCaptor; @@ -126,8 +127,7 @@ private void compareFields(Set expected, Set reserved) { } public void testMappingRequiresUpdateNoMapping() { - ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name")); - ClusterState cs = csBuilder.build(); + ClusterState cs = ClusterState.builder(new ClusterName("_name")).build(); String[] indices = new String[] { "no_index" }; assertArrayEquals(new String[] { "no_index" }, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); @@ -145,86 +145,139 @@ public void testMappingRequiresUpdateNoVersion() { assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); } - public void testMappingRequiresUpdateRecentMappingVersion() { + /** + * In 8.10 we switched from using the product version to using the per-index mappings + * version to determine whether mappings need updating. So any case of the per-index + * mappings version not being present should result in the mappings being updated. + */ + public void testMappingRequiresUpdateOnlyProductVersion() { + int newVersion = randomIntBetween(1, 100); { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_current", Version.CURRENT.toString()) + Collections.singletonMap("product_version_only", MlIndexAndAlias.BWC_MAPPINGS_VERSION) ); - String[] indices = new String[] { "version_current" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + String[] indices = new String[] { "product_version_only" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); + } + { + ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "8.10.2")); + String[] indices = new String[] { "product_version_only" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); + } + { + ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "7.17.13")); + String[] indices = new String[] { "product_version_only" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); + } + { + // Serverless versions may be build hashes + ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "a1b2c3d4")); + String[] indices = new String[] { "product_version_only" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); } + } + + /** + * In 8.10 we switched from using the product version to using the per-index mappings + * version to determine whether mappings need updating. The per-index mappings version + * should determine the need for update regardless of the value of the product version + * field. + */ + public void testMappingRequiresUpdateGivenProductVersionAndMappingsVersion() { + int currentVersion = randomIntBetween(1, 100); + int newVersion = currentVersion + randomIntBetween(1, 100); { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_current", Version.CURRENT.toString()), - Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1) + Collections.singletonMap("both", MlIndexAndAlias.BWC_MAPPINGS_VERSION), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) ); - String[] indices = new String[] { "version_current" }; - assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + String[] indices = new String[] { "both" }; + assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion)); } { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_current", Version.CURRENT.toString()), - Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1) + Collections.singletonMap("both", "8.10.2"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) ); - String[] indices = new String[] { "version_current" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 2)); + String[] indices = new String[] { "both" }; + assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion)); } - } - - public void testMappingRequiresUpdateMaliciousMappingVersion() { - ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_current", Collections.singletonMap("nested", "1.0")) - ); - String[] indices = new String[] { "version_nested" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); - } - - public void testMappingRequiresUpdateBogusMappingVersion() { - ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_bogus", "0.0")); - String[] indices = new String[] { "version_bogus" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); - } - - public void testMappingRequiresUpdateNewerMappingVersion() { { - ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_newer", 2)); - String[] indices = new String[] { "version_newer" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + // Serverless versions may be build hashes + ClusterState cs = getClusterStateWithMappingsWithMetadata( + Collections.singletonMap("both", "a1b2c3d4"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) + ); + String[] indices = new String[] { "both" }; + assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion)); } { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_newer", Version.CURRENT), - Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 2) + Collections.singletonMap("both", MlIndexAndAlias.BWC_MAPPINGS_VERSION), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) ); - String[] indices = new String[] { "version_newer" }; - assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + String[] indices = new String[] { "both" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); } { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_newer", Version.CURRENT), - Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1) + Collections.singletonMap("both", "8.10.2"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) ); - String[] indices = new String[] { "version_newer" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 2)); + String[] indices = new String[] { "both" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); + } + { + // Serverless versions may be build hashes + ClusterState cs = getClusterStateWithMappingsWithMetadata( + Collections.singletonMap("both", "a1b2c3d4"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) + ); + String[] indices = new String[] { "both" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); } } - public void testMappingRequiresUpdateNewerMappingVersionMinor() { + /** + * In 8.10 we switched from using the product version to using the per-index mappings + * version to determine whether mappings need updating. The per-index mappings version + * should determine the need for update even if the product version field is not present. + */ + public void testMappingRequiresUpdateGivenOnlyMappingsVersion() { + int currentVersion = randomIntBetween(1, 100); + int newVersion = currentVersion + randomIntBetween(1, 100); { - ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_newer_minor", 1)); - String[] indices = new String[] { "version_newer_minor" }; - assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + ClusterState cs = getClusterStateWithMappingsWithMetadata( + Collections.singletonMap("mappings_version_only", "NO_VERSION_FIELD"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) + ); + String[] indices = new String[] { "mappings_version_only" }; + assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion)); } { ClusterState cs = getClusterStateWithMappingsWithMetadata( - Collections.singletonMap("version_newer_minor", Version.CURRENT), - Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1) + Collections.singletonMap("mappings_version_only", "NO_VERSION_FIELD"), + Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion) ); - String[] indices = new String[] { "version_newer_minor" }; - assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + String[] indices = new String[] { "mappings_version_only" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion)); } } + public void testMappingRequiresUpdateMaliciousMappingVersion() { + ClusterState cs = getClusterStateWithMappingsWithMetadata( + Collections.singletonMap("version_nested", Collections.singletonMap("nested", "1.0")) + ); + String[] indices = new String[] { "version_nested" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + } + + public void testMappingRequiresUpdateBogusMappingVersion() { + ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_bogus", "0.0")); + String[] indices = new String[] { "version_bogus" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1)); + } + @SuppressWarnings({ "unchecked" }) public void testAddDocMappingIfMissing() { ThreadPool threadPool = mock(ThreadPool.class); @@ -287,7 +340,7 @@ private ClusterState getClusterStateWithMappingsWithMetadata(Map meta.put("version", version); } if (metaData != null) { - metaData.forEach((k, v) -> meta.putIfAbsent(k, v)); + metaData.forEach(meta::putIfAbsent); } mapping.put("_meta", meta); diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobResultsProviderIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobResultsProviderIT.java index 909033a8befe6..fb639e55fe306 100644 --- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobResultsProviderIT.java +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobResultsProviderIT.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.ml.integration; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; @@ -64,6 +63,7 @@ import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSnapshot; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.Quantiles; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.TimingStats; +import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias; import org.elasticsearch.xpack.core.ml.utils.ToXContentParams; import org.elasticsearch.xpack.ml.MlSingleNodeTestCase; import org.elasticsearch.xpack.ml.inference.ingest.InferenceProcessor; @@ -519,7 +519,8 @@ private Map getIndexMappingProperties(String index) { @SuppressWarnings("unchecked") Map meta = (Map) mappings.get("_meta"); assertThat(meta.keySet(), hasItem("version")); - assertThat(meta.get("version"), equalTo(Version.CURRENT.toString())); + assertThat(meta.get("version"), equalTo(MlIndexAndAlias.BWC_MAPPINGS_VERSION)); + assertThat(meta.get("managed_index_mappings_version"), equalTo(AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION)); @SuppressWarnings("unchecked") Map properties = (Map) mappings.get("properties"); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java index 95d543a0f63ea..c8eaa9e78f2ef 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.upgrades; -import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; @@ -131,7 +130,6 @@ private void assertUpgradedResultsMappings() throws Exception { } assertNotNull(indexLevel); - assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel)); assertEquals( AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION, extractValue("mappings._meta.managed_index_mappings_version", indexLevel) @@ -168,7 +166,6 @@ private void assertUpgradedAnnotationsMappings() throws Exception { } assertNotNull(indexLevel); - assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel)); assertEquals( AnnotationIndex.ANNOTATION_INDEX_MAPPINGS_VERSION, extractValue("mappings._meta.managed_index_mappings_version", indexLevel) @@ -226,7 +223,6 @@ private void assertUpgradedConfigMappings() throws Exception { Map indexLevel = (Map) responseLevel.get(".ml-config"); assertNotNull(indexLevel); - assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel)); assertEquals( MlConfigIndex.CONFIG_INDEX_MAPPINGS_VERSION, extractValue("mappings._meta.managed_index_mappings_version", indexLevel) From 7fdba1a28322662ceb0dec4fe5337610cfe79d06 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 27 Sep 2023 20:31:58 +0300 Subject: [PATCH 105/155] ESQL: Preserve the projection of an aggregation (#99936) The internal AggregateOperators are focused creating groups and performing the aggregations using their intermediate states. However the logical projection is not enforced leading to errors downstream. Currently this is handled through OutputOperator which forces an alignment between the layout and the attributes however this is not only fragile but also insufficient. This commit fixes a couple of things: - introduces a ProjectOperator in front of the AggregateOperator. This is much easier, at least at the moment as it keeps the agg logic untouched. - improves the ProjectOperator by moving away from the BitSet to a List that handles not just the selection but also ordering Fix #99782 --- .../compute/operator/AggregationOperator.java | 1 + .../compute/operator/ProjectOperator.java | 62 ++++++++++++------- .../operator/ProjectOperatorTests.java | 31 ++++------ .../xpack/esql/CsvTestUtils.java | 14 +++-- .../src/main/resources/stats.csv-spec | 32 ++++++++++ .../xpack/esql/action/EsqlQueryResponse.java | 5 +- .../esql/enrich/EnrichLookupService.java | 13 ++-- .../esql/planner/LocalExecutionPlanner.java | 34 +++++----- 8 files changed, 119 insertions(+), 73 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AggregationOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AggregationOperator.java index d2cc1b3322cce..fc26c829c5757 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AggregationOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/AggregationOperator.java @@ -90,6 +90,7 @@ public void finish() { } finished = true; int[] aggBlockCounts = aggregators.stream().mapToInt(Aggregator::evaluateBlockCount).toArray(); + // TODO: look into allocating the blocks lazily Block[] blocks = new Block[Arrays.stream(aggBlockCounts).sum()]; int offset = 0; for (int i = 0; i < aggregators.size(); i++) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java index ec0edf5e3ed9c..2cace48f155d2 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/ProjectOperator.java @@ -14,65 +14,85 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ProjectOperator extends AbstractPageMappingOperator { - private final BitSet bs; + private final Set pagesUsed; + private final int[] projection; private Block[] blocks; - public record ProjectOperatorFactory(BitSet mask) implements OperatorFactory { + public record ProjectOperatorFactory(List projection) implements OperatorFactory { @Override public Operator get(DriverContext driverContext) { - return new ProjectOperator(mask); + return new ProjectOperator(projection); } @Override public String describe() { - return "ProjectOperator[mask = " + mask + "]"; + return "ProjectOperator[projection = " + projection + "]"; } } /** - * Creates a project that applies the given mask (as a bitset). + * Creates an operator that applies the given projection, encoded as an integer list where + * the ordinal indicates the output order and the value, the backing channel that to be used. + * Given the input {a,b,c,d}, project {a,d,a} is encoded as {0,3,0}. * - * @param mask bitset mask for enabling/disabling blocks / columns inside a Page + * @param projection list of blocks to keep and their order. */ - public ProjectOperator(BitSet mask) { - this.bs = mask; + public ProjectOperator(List projection) { + this.pagesUsed = new HashSet<>(projection); + this.projection = projection.stream().mapToInt(Integer::intValue).toArray(); } @Override protected Page process(Page page) { - if (page.getBlockCount() == 0) { + var blockCount = page.getBlockCount(); + if (blockCount == 0) { return page; } if (blocks == null) { - blocks = new Block[bs.cardinality()]; + blocks = new Block[projection.length]; } Arrays.fill(blocks, null); int b = 0; - int positionCount = page.getPositionCount(); + for (int source : projection) { + if (source >= blockCount) { + throw new IllegalArgumentException( + "Cannot project block with index [" + source + "] from a page with size [" + blockCount + "]" + ); + } + var block = page.getBlock(source); + blocks[b++] = block; + } + // iterate the blocks to see which one isn't used List blocksToRelease = new ArrayList<>(); - for (int i = 0; i < page.getBlockCount(); i++) { - var block = page.getBlock(i); - if (bs.get(i)) { - assertNotReleasing(blocksToRelease, block); - blocks[b++] = block; - } else { - blocksToRelease.add(block); + + for (int i = 0; i < blockCount; i++) { + boolean used = false; + var current = page.getBlock(i); + for (int j = 0; j < blocks.length; j++) { + if (current == blocks[j]) { + used = true; + break; + } + } + if (used == false) { + blocksToRelease.add(current); } } Releasables.close(blocksToRelease); - return new Page(positionCount, blocks); + return new Page(page.getPositionCount(), blocks); } @Override public String toString() { - return "ProjectOperator[mask = " + bs + ']'; + return "ProjectOperator[projection = " + Arrays.toString(projection) + ']'; } static void assertNotReleasing(List toRelease, Block toKeep) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index d4d7095d92f7b..1d3c95ff5eba3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -22,12 +22,13 @@ import org.junit.After; import org.junit.Before; -import java.util.BitSet; +import java.util.Arrays; import java.util.List; import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -50,7 +51,7 @@ protected DriverContext driverContext() { public void testProjectionOnEmptyPage() { var page = new Page(0); - var projection = new ProjectOperator(randomMask(randomIntBetween(2, 10))); + var projection = new ProjectOperator(randomProjection(10)); projection.addInput(page); assertEquals(page, projection.getOutput()); } @@ -63,30 +64,22 @@ public void testProjection() { } var page = new Page(size, blocks); - var mask = randomMask(size); + var randomProjection = randomProjection(size); - var projection = new ProjectOperator(mask); + var projection = new ProjectOperator(randomProjection); projection.addInput(page); var out = projection.getOutput(); - assertEquals(mask.cardinality(), out.getBlockCount()); + assertThat(randomProjection.size(), lessThanOrEqualTo(out.getBlockCount())); - int lastSetIndex = -1; for (int i = 0; i < out.getBlockCount(); i++) { var block = out.getBlock(i); - var shouldBeSetInMask = block.getInt(0); - assertTrue(mask.get(shouldBeSetInMask)); - lastSetIndex = mask.nextSetBit(lastSetIndex + 1); - assertEquals(shouldBeSetInMask, lastSetIndex); + assertEquals(block, page.getBlock(randomProjection.get(i))); block.close(); } } - private BitSet randomMask(int size) { - var mask = new BitSet(size); - for (int i = 0; i < size; i++) { - mask.set(i, randomBoolean()); - } - return mask; + private List randomProjection(int size) { + return randomList(size, () -> randomIntBetween(0, size - 1)); } @Override @@ -96,14 +89,12 @@ protected SourceOperator simpleInput(int end) { @Override protected Operator.OperatorFactory simple(BigArrays bigArrays) { - BitSet mask = new BitSet(); - mask.set(1, true); - return new ProjectOperator.ProjectOperatorFactory(mask); + return new ProjectOperator.ProjectOperatorFactory(Arrays.asList(1)); } @Override protected String expectedDescriptionOfSimple() { - return "ProjectOperator[mask = {1}]"; + return "ProjectOperator[projection = [1]]"; } @Override diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java index 7fafa794653e5..b58a7770eef10 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java @@ -441,21 +441,23 @@ static void logMetaData(List actualColumnNames, List actualColumnT logger.info(sb.toString()); } - static void logData(List> values, Logger logger) { - for (List list : values) { - logger.info(rowAsString(list)); + static void logData(Iterator> values, Logger logger) { + while (values.hasNext()) { + var val = values.next(); + logger.info(rowAsString(val)); } } - private static String rowAsString(List list) { + private static String rowAsString(Iterator iterator) { StringBuilder sb = new StringBuilder(); StringBuilder column = new StringBuilder(); - for (int i = 0; i < list.size(); i++) { + for (int i = 0; iterator.hasNext(); i++) { column.setLength(0); if (i > 0) { sb.append(" | "); } - sb.append(trimOrPad(column.append(list.get(i)))); + var next = iterator.next(); + sb.append(trimOrPad(column.append(next))); } return sb.toString(); } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec index 89e4c3b2b8174..55016a4cd2dc2 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec @@ -413,6 +413,36 @@ c:long | languages:integer | still_hired:boolean 4 | null | true ; +byUnmentionedIntAndBooleanFollowedByProjection +from employees | stats c = count(gender) by languages, still_hired | where languages > 3 | sort languages | keep languages; + +languages:integer + 4 + 4 + 5 + 5 +; + +byTwoGroupReturnedInDifferentOrder +from employees | stats c = count(emp_no) by gender, languages | rename languages as l, gender as g | where l > 3 | keep g, l | sort g, l; + +g:keyword | l:integer + F | 4 + F | 5 + M | 4 + M | 5 + null | 4 + null | 5 +; + +repetitiveAggregation +from employees | stats m1 = max(salary), m2 = min(salary), m3 = min(salary), m4 = max(salary); + +m1:i | m2:i | m3:i | m4:i +74999| 25324| 25324| 74999 +; + + byDateAndKeywordAndInt from employees | eval d = date_trunc(1 year, hire_date) | stats c = count(emp_no) by d, gender, languages | sort c desc, d, languages desc, gender desc | limit 10; @@ -501,3 +531,5 @@ from employees | limit 10 | eval x = 1 | stats c = count(x); c:l 10 ; + + diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java index 796f009cfd3bb..4aadd7ec8e986 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlQueryResponse.java @@ -213,13 +213,14 @@ public static Iterator> pagesToValues(List dataTypes, L */ int count = block.getValueCount(p); int start = block.getFirstValueIndex(p); + String dataType = dataTypes.get(b); if (count == 1) { - return valueAt(dataTypes.get(b), block, start, scratch); + return valueAt(dataType, block, start, scratch); } List thisResult = new ArrayList<>(count); int end = count + start; for (int i = start; i < end; i++) { - thisResult.add(valueAt(dataTypes.get(b), block, i, scratch)); + thisResult.add(valueAt(dataType, block, i, scratch)); } return thisResult; })) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index be75c0d1c05d6..ba07a688387cc 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -66,7 +66,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.BitSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -245,10 +244,14 @@ private static Page createNullResponse(int positionCount, List } private static Operator droppingBlockOperator(int totalBlocks, int droppingPosition) { - BitSet bitSet = new BitSet(totalBlocks); - bitSet.set(0, totalBlocks); - bitSet.clear(droppingPosition); - return new ProjectOperator(bitSet); + var size = totalBlocks - 1; + var projection = new ArrayList(size); + for (int i = 0; i < totalBlocks; i++) { + if (i != droppingPosition) { + projection.add(i); + } + } + return new ProjectOperator(projection); } private class TransportHandler implements TransportRequestHandler { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java index 700edcf5582c2..18fad8cecb014 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java @@ -85,7 +85,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -152,6 +151,12 @@ public LocalExecutionPlan plan(PhysicalPlan node) { blockFactory ); + // workaround for https://github.com/elastic/elasticsearch/issues/99782 + node = node.transformUp( + AggregateExec.class, + a -> a.getMode() == AggregateExec.Mode.FINAL ? new ProjectExec(a.source(), a, Expressions.asAttributes(a.aggregates())) : a + ); + PhysicalOperation physicalOperation = plan(node, context); context.addDriverFactory( @@ -506,9 +511,14 @@ private PhysicalOperation planShow(ShowExec showExec) { private PhysicalOperation planProject(ProjectExec project, LocalExecutionPlannerContext context) { var source = plan(project.child(), context); + List projections = project.projections(); + List projectionList = new ArrayList<>(projections.size()); + Layout.Builder layout = new Layout.Builder(); Map inputChannelToOutputIds = new HashMap<>(); - for (NamedExpression ne : project.projections()) { + for (int index = 0, size = projections.size(); index < size; index++) { + NamedExpression ne = projections.get(index); + NameId inputId; if (ne instanceof Alias a) { inputId = ((NamedExpression) a.child()).id(); @@ -524,26 +534,12 @@ private PhysicalOperation planProject(ProjectExec project, LocalExecutionPlanner throw new IllegalArgumentException("type mismatch for aliases"); } channelSet.nameIds().add(ne.id()); - } - BitSet mask = new BitSet(); - Layout.Builder layout = new Layout.Builder(); - - for (int inChannel = 0; inChannel < source.layout.numberOfChannels(); inChannel++) { - Layout.ChannelSet outputSet = inputChannelToOutputIds.get(inChannel); - - if (outputSet != null) { - mask.set(inChannel); - layout.append(outputSet); - } + layout.append(channelSet); + projectionList.add(input.channel()); } - if (mask.cardinality() == source.layout.numberOfChannels()) { - // all columns are retained, project operator is not needed but the layout needs to be updated - return source.with(layout.build()); - } else { - return source.with(new ProjectOperatorFactory(mask), layout.build()); - } + return source.with(new ProjectOperatorFactory(projectionList), layout.build()); } private PhysicalOperation planFilter(FilterExec filter, LocalExecutionPlannerContext context) { From 4475fddbf75969efa2e1994297d4b34c828491c0 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 27 Sep 2023 11:36:28 -0700 Subject: [PATCH 106/155] ESQL: Improve block closing in tests --- .../elasticsearch/compute/operator/ProjectOperatorTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index 1d3c95ff5eba3..fa3b2096acb05 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -74,8 +74,9 @@ public void testProjection() { for (int i = 0; i < out.getBlockCount(); i++) { var block = out.getBlock(i); assertEquals(block, page.getBlock(randomProjection.get(i))); - block.close(); } + // close all blocks separately since the same block can be used by multiple columns (aliased) + out.releaseBlocks(); } private List randomProjection(int size) { From dd1cb826f74f98aa2094f1d1cbfcac0b97dec27a Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 27 Sep 2023 15:18:01 -0400 Subject: [PATCH 107/155] ESQL: Fixed length vector builder (#99970) This adds things like `IntVector.FixedBuilder` which is slightly simpler to use than constructing the arrays by hand. It also measures bytes used up front in the circuit breaker. And it'll be easier to integrate it into framework happening over in #99931 to handle errors in topn. This also uses it in `mv_` functions. --- x-pack/plugin/esql/compute/build.gradle | 21 +++ .../compute/gen/MvEvaluatorImplementer.java | 49 +++---- .../compute/data/BooleanArrayVector.java | 2 +- .../compute/data/BooleanVector.java | 28 ++++ .../compute/data/BooleanVectorBuilder.java | 2 +- .../data/BooleanVectorFixedBuilder.java | 61 ++++++++ .../compute/data/BytesRefArrayVector.java | 2 +- .../compute/data/BytesRefVector.java | 7 + .../compute/data/BytesRefVectorBuilder.java | 2 +- .../compute/data/ConstantBooleanVector.java | 4 +- .../compute/data/ConstantBytesRefVector.java | 6 +- .../compute/data/ConstantDoubleVector.java | 4 +- .../compute/data/ConstantIntVector.java | 4 +- .../compute/data/ConstantLongVector.java | 4 +- .../compute/data/DoubleArrayVector.java | 2 +- .../compute/data/DoubleVector.java | 28 ++++ .../compute/data/DoubleVectorBuilder.java | 2 +- .../data/DoubleVectorFixedBuilder.java | 61 ++++++++ .../compute/data/IntArrayVector.java | 2 +- .../elasticsearch/compute/data/IntVector.java | 28 ++++ .../compute/data/IntVectorBuilder.java | 2 +- .../compute/data/IntVectorFixedBuilder.java | 61 ++++++++ .../compute/data/LongArrayVector.java | 2 +- .../compute/data/LongVector.java | 28 ++++ .../compute/data/LongVectorBuilder.java | 2 +- .../compute/data/LongVectorFixedBuilder.java | 61 ++++++++ .../compute/data/BlockFactory.java | 18 ++- .../compute/data/X-ArrayVector.java.st | 2 +- .../compute/data/X-ConstantVector.java.st | 13 +- .../compute/data/X-Vector.java.st | 40 ++++++ .../compute/data/X-VectorBuilder.java.st | 2 +- .../compute/data/X-VectorFixedBuilder.java.st | 61 ++++++++ .../compute/operator/DriverContext.java | 2 +- .../compute/data/BlockFactoryTests.java | 4 + .../compute/data/VectorFixedBuilderTests.java | 136 ++++++++++++++++++ .../xpack/esql/action/EsqlDisruptionIT.java | 4 +- .../multivalue/MvAvgDoubleEvaluator.java | 10 +- .../scalar/multivalue/MvAvgIntEvaluator.java | 20 +-- .../scalar/multivalue/MvAvgLongEvaluator.java | 20 +-- .../MvAvgUnsignedLongEvaluator.java | 20 +-- .../multivalue/MvMaxBooleanEvaluator.java | 18 +-- .../multivalue/MvMaxBytesRefEvaluator.java | 20 ++- .../multivalue/MvMaxDoubleEvaluator.java | 18 +-- .../scalar/multivalue/MvMaxIntEvaluator.java | 18 +-- .../scalar/multivalue/MvMaxLongEvaluator.java | 18 +-- .../multivalue/MvMedianDoubleEvaluator.java | 10 +- .../multivalue/MvMedianIntEvaluator.java | 18 +-- .../multivalue/MvMedianLongEvaluator.java | 18 +-- .../MvMedianUnsignedLongEvaluator.java | 18 +-- .../multivalue/MvMinBooleanEvaluator.java | 18 +-- .../multivalue/MvMinBytesRefEvaluator.java | 20 ++- .../multivalue/MvMinDoubleEvaluator.java | 18 +-- .../scalar/multivalue/MvMinIntEvaluator.java | 18 +-- .../scalar/multivalue/MvMinLongEvaluator.java | 18 +-- .../multivalue/MvSumDoubleEvaluator.java | 10 +- .../scalar/multivalue/MvSumIntEvaluator.java | 2 +- .../scalar/multivalue/MvSumLongEvaluator.java | 2 +- .../MvSumUnsignedLongEvaluator.java | 2 +- .../evaluator/mapper/EvaluatorMapper.java | 16 ++- 59 files changed, 879 insertions(+), 228 deletions(-) create mode 100644 x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java create mode 100644 x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java create mode 100644 x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java create mode 100644 x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st create mode 100644 x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java diff --git a/x-pack/plugin/esql/compute/build.gradle b/x-pack/plugin/esql/compute/build.gradle index cd4b131a5b0fe..7215b53a1a4f0 100644 --- a/x-pack/plugin/esql/compute/build.gradle +++ b/x-pack/plugin/esql/compute/build.gradle @@ -343,6 +343,27 @@ tasks.named('stringTemplates').configure { it.inputFile = vectorBuildersInputFile it.outputFile = "org/elasticsearch/compute/data/BooleanVectorBuilder.java" } + File vectorFixedBuildersInputFile = new File("${projectDir}/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st") + template { + it.properties = intProperties + it.inputFile = vectorFixedBuildersInputFile + it.outputFile = "org/elasticsearch/compute/data/IntVectorFixedBuilder.java" + } + template { + it.properties = longProperties + it.inputFile = vectorFixedBuildersInputFile + it.outputFile = "org/elasticsearch/compute/data/LongVectorFixedBuilder.java" + } + template { + it.properties = doubleProperties + it.inputFile = vectorFixedBuildersInputFile + it.outputFile = "org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java" + } + template { + it.properties = booleanProperties + it.inputFile = vectorFixedBuildersInputFile + it.outputFile = "org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java" + } File stateInputFile = new File("${projectDir}/src/main/java/org/elasticsearch/compute/aggregation/X-State.java.st") template { it.properties = intProperties diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java index 9547548ba42ae..86ae6d3f46789 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java @@ -31,17 +31,15 @@ import static org.elasticsearch.compute.gen.Methods.getMethod; import static org.elasticsearch.compute.gen.Types.ABSTRACT_MULTIVALUE_FUNCTION_EVALUATOR; import static org.elasticsearch.compute.gen.Types.ABSTRACT_NULLABLE_MULTIVALUE_FUNCTION_EVALUATOR; -import static org.elasticsearch.compute.gen.Types.BIG_ARRAYS; import static org.elasticsearch.compute.gen.Types.BLOCK; import static org.elasticsearch.compute.gen.Types.BYTES_REF; -import static org.elasticsearch.compute.gen.Types.BYTES_REF_ARRAY; import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; import static org.elasticsearch.compute.gen.Types.SOURCE; import static org.elasticsearch.compute.gen.Types.VECTOR; import static org.elasticsearch.compute.gen.Types.WARNINGS; -import static org.elasticsearch.compute.gen.Types.arrayVectorType; import static org.elasticsearch.compute.gen.Types.blockType; +import static org.elasticsearch.compute.gen.Types.vectorType; public class MvEvaluatorImplementer { private final TypeElement declarationType; @@ -197,16 +195,25 @@ private MethodSpec evalShell( builder.addStatement("int positionCount = v.getPositionCount()"); if (nullable) { TypeName resultBlockType = blockType(resultType); - builder.addStatement("$T.Builder builder = $T.newBlockBuilder(positionCount)", resultBlockType, resultBlockType); + builder.addStatement( + "$T.Builder builder = $T.newBlockBuilder(positionCount, driverContext.blockFactory())", + resultBlockType, + resultBlockType + ); } else if (resultType.equals(BYTES_REF)) { + TypeName resultVectorType = vectorType(resultType); builder.addStatement( - "$T values = new $T(positionCount, $T.NON_RECYCLING_INSTANCE)", // TODO blocks should use recycling array - BYTES_REF_ARRAY, - BYTES_REF_ARRAY, - BIG_ARRAYS + "$T.Builder builder = $T.newVectorBuilder(positionCount, driverContext.blockFactory())", + resultVectorType, + resultVectorType ); } else { - builder.addStatement("$T[] values = new $T[positionCount]", resultType, resultType); + TypeName resultVectorType = vectorType(resultType); + builder.addStatement( + "$T.FixedBuilder builder = $T.newVectorFixedBuilder(positionCount, driverContext.blockFactory())", + resultVectorType, + resultVectorType + ); } if (false == workType.equals(fieldType) && workType.isPrimitive() == false) { @@ -244,11 +251,7 @@ private MethodSpec evalShell( } builder.endControlFlow(); - if (nullable) { - builder.addStatement("return builder.build()"); - } else { - builder.addStatement("return new $T(values, positionCount)", arrayVectorType(resultType)); - } + builder.addStatement("return builder.build()"); return builder.build(); } @@ -268,7 +271,7 @@ private MethodSpec eval(String name, boolean nullable) { builder.beginControlFlow("if (valueCount == 1)"); fetch(builder, "value", fieldType, "first", workType.equals(fieldType) ? "firstScratch" : "valueScratch"); singleValueFunction.call(builder); - writeResult(builder, nullable); + writeResult(builder); builder.addStatement("continue"); builder.endControlFlow(); } @@ -302,7 +305,7 @@ private MethodSpec eval(String name, boolean nullable) { builder.endControlFlow(); finishFunction.call(builder, "work"); } - writeResult(builder, nullable); + writeResult(builder); }); } @@ -313,7 +316,7 @@ private MethodSpec evalSingleValued(String name, boolean nullable) { builder.addStatement("int first = v.getFirstValueIndex(p)"); fetch(builder, "value", fieldType, "first", workType.equals(fieldType) ? "firstScratch" : "valueScratch"); singleValueFunction.call(builder); - writeResult(builder, nullable); + writeResult(builder); }); } @@ -332,17 +335,15 @@ private MethodSpec evalAscending(String name, boolean nullable) { return evalShell(name, false, nullable, javadoc, builder -> {}, builder -> { builder.addStatement("int first = v.getFirstValueIndex(p)"); ascendingFunction.call(builder); - writeResult(builder, nullable); + writeResult(builder); }); } - private void writeResult(MethodSpec.Builder builder, boolean nullable) { - if (nullable) { - builder.addStatement("builder.$L(result)", appendMethod(resultType)); - } else if (fieldType.equals(BYTES_REF)) { - builder.addStatement("values.append(result)"); + private void writeResult(MethodSpec.Builder builder) { + if (fieldType.equals(BYTES_REF)) { + builder.addStatement("builder.appendBytesRef(result)"); } else { - builder.addStatement("values[p] = result"); + builder.addStatement("builder.$L(result)", appendMethod(resultType)); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java index 840f965ff6806..53e88dfac8739 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayVector.java @@ -17,7 +17,7 @@ */ public final class BooleanArrayVector extends AbstractVector implements BooleanVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BooleanArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BooleanArrayVector.class); private final boolean[] values; diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java index 477c8310e9708..79b5ec40b81e5 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java @@ -106,10 +106,25 @@ static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} + * if you know the size up front because it's faster. + */ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBooleanVectorBuilder(estimatedSize); } + /** + * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} + * if you know the size up front because it's faster. + */ + static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { + return blockFactory.newBooleanVectorFixedBuilder(size); + } + + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits BooleanVectorBuilder { /** * Appends a boolean to the current entry. @@ -119,4 +134,17 @@ sealed interface Builder extends Vector.Builder permits BooleanVectorBuilder { @Override BooleanVector build(); } + + /** + * A builder that never grows. + */ + sealed interface FixedBuilder extends Vector.Builder permits BooleanVectorFixedBuilder { + /** + * Appends a boolean to the current entry. + */ + FixedBuilder appendBoolean(boolean value); + + @Override + BooleanVector build(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java index d9926227e1c60..45c74ee6e06d4 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java @@ -10,7 +10,7 @@ import java.util.Arrays; /** - * Block build of BooleanBlocks. + * Builder for {@link BooleanVector}s that grows as needed. * This class is generated. Do not edit it. */ final class BooleanVectorBuilder extends AbstractVectorBuilder implements BooleanVector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java new file mode 100644 index 0000000000000..30146d4e55c02 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.RamUsageEstimator; + +/** + * Builder for {@link BooleanVector}s that never grows. Prefer this to + * {@link BooleanVectorBuilder} if you know the precise size up front because + * it's faster. + * This class is generated. Do not edit it. + */ +final class BooleanVectorFixedBuilder implements BooleanVector.FixedBuilder { + private final BlockFactory blockFactory; + private final boolean[] values; + /** + * The next value to write into. {@code -1} means the vector has already + * been built. + */ + private int nextIndex; + + BooleanVectorFixedBuilder(int size, BlockFactory blockFactory) { + blockFactory.adjustBreaker(ramBytesUsed(size), false); + this.blockFactory = blockFactory; + this.values = new boolean[size]; + } + + @Override + public BooleanVectorFixedBuilder appendBoolean(boolean value) { + values[nextIndex++] = value; + return this; + } + + private static long ramBytesUsed(int size) { + return size == 1 + ? ConstantBooleanVector.RAM_BYTES_USED + : BooleanArrayVector.BASE_RAM_BYTES_USED + RamUsageEstimator.alignObjectSize( + (long) RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + size * Byte.BYTES + ); + } + + @Override + public BooleanVector build() { + if (nextIndex < 0) { + throw new IllegalStateException("already closed"); + } + if (nextIndex != values.length) { + throw new IllegalStateException("expected to write [" + values.length + "] entries but wrote [" + nextIndex + "]"); + } + nextIndex = -1; + if (values.length == 1) { + return new ConstantBooleanVector(values[0], 1, blockFactory); + } + return new BooleanArrayVector(values, values.length, blockFactory); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java index fc32519a6acce..c8f5276a99db5 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java @@ -18,7 +18,7 @@ */ public final class BytesRefArrayVector extends AbstractVector implements BytesRefVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BytesRefArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(BytesRefArrayVector.class); private final BytesRefArray values; diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java index ffe0b06d1f430..84cb24f955618 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java @@ -106,10 +106,16 @@ static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Creates a builder that grows as needed. + */ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBytesRefVectorBuilder(estimatedSize); } + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits BytesRefVectorBuilder { /** * Appends a BytesRef to the current entry. @@ -119,4 +125,5 @@ sealed interface Builder extends Vector.Builder permits BytesRefVectorBuilder { @Override BytesRefVector build(); } + } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java index be753771ac961..f37ffb2a7e28a 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java @@ -13,7 +13,7 @@ import org.elasticsearch.core.Releasables; /** - * Block build of BytesRefBlocks. + * Builder for {@link BytesRefVector}s that grows as needed. * This class is generated. Do not edit it. */ final class BytesRefVectorBuilder extends AbstractVectorBuilder implements BytesRefVector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java index 7119721811401..4a37ad72f45ae 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBooleanVector.java @@ -15,7 +15,7 @@ */ public final class ConstantBooleanVector extends AbstractVector implements BooleanVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantBooleanVector.class); + static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantBooleanVector.class); private final boolean value; @@ -55,7 +55,7 @@ public boolean isConstant() { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance(boolean.class); + return RAM_BYTES_USED; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java index caa30a5a2148c..c32335aba3498 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantBytesRefVector.java @@ -16,8 +16,8 @@ */ public final class ConstantBytesRefVector extends AbstractVector implements BytesRefVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantBytesRefVector.class); - + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantBytesRefVector.class) + RamUsageEstimator + .shallowSizeOfInstance(BytesRef.class); private final BytesRef value; public ConstantBytesRefVector(BytesRef value, int positionCount) { @@ -56,7 +56,7 @@ public boolean isConstant() { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance(BytesRef.class); + return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(value.bytes); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java index be41df1188ea0..8cd5de0b8933b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantDoubleVector.java @@ -15,7 +15,7 @@ */ public final class ConstantDoubleVector extends AbstractVector implements DoubleVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantDoubleVector.class); + static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantDoubleVector.class); private final double value; @@ -55,7 +55,7 @@ public boolean isConstant() { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance(double.class); + return RAM_BYTES_USED; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java index 4854db91fe567..4261f31aa218b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantIntVector.java @@ -15,7 +15,7 @@ */ public final class ConstantIntVector extends AbstractVector implements IntVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantIntVector.class); + static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantIntVector.class); private final int value; @@ -55,7 +55,7 @@ public boolean isConstant() { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance(int.class); + return RAM_BYTES_USED; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java index 1f33d97e9c39d..56222191c355d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/ConstantLongVector.java @@ -15,7 +15,7 @@ */ public final class ConstantLongVector extends AbstractVector implements LongVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantLongVector.class); + static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantLongVector.class); private final long value; @@ -55,7 +55,7 @@ public boolean isConstant() { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance(long.class); + return RAM_BYTES_USED; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java index 44bf852f628ca..79df19c746e4d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayVector.java @@ -17,7 +17,7 @@ */ public final class DoubleArrayVector extends AbstractVector implements DoubleVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(DoubleArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(DoubleArrayVector.class); private final double[] values; diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java index 8461f36fb9e7a..ce3e1ffa291f4 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java @@ -107,10 +107,25 @@ static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} + * if you know the size up front because it's faster. + */ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newDoubleVectorBuilder(estimatedSize); } + /** + * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} + * if you know the size up front because it's faster. + */ + static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { + return blockFactory.newDoubleVectorFixedBuilder(size); + } + + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits DoubleVectorBuilder { /** * Appends a double to the current entry. @@ -120,4 +135,17 @@ sealed interface Builder extends Vector.Builder permits DoubleVectorBuilder { @Override DoubleVector build(); } + + /** + * A builder that never grows. + */ + sealed interface FixedBuilder extends Vector.Builder permits DoubleVectorFixedBuilder { + /** + * Appends a double to the current entry. + */ + FixedBuilder appendDouble(double value); + + @Override + DoubleVector build(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java index 8112c5458280f..f92ec67aec012 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java @@ -10,7 +10,7 @@ import java.util.Arrays; /** - * Block build of DoubleBlocks. + * Builder for {@link DoubleVector}s that grows as needed. * This class is generated. Do not edit it. */ final class DoubleVectorBuilder extends AbstractVectorBuilder implements DoubleVector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java new file mode 100644 index 0000000000000..83992ed71b720 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.RamUsageEstimator; + +/** + * Builder for {@link DoubleVector}s that never grows. Prefer this to + * {@link DoubleVectorBuilder} if you know the precise size up front because + * it's faster. + * This class is generated. Do not edit it. + */ +final class DoubleVectorFixedBuilder implements DoubleVector.FixedBuilder { + private final BlockFactory blockFactory; + private final double[] values; + /** + * The next value to write into. {@code -1} means the vector has already + * been built. + */ + private int nextIndex; + + DoubleVectorFixedBuilder(int size, BlockFactory blockFactory) { + blockFactory.adjustBreaker(ramBytesUsed(size), false); + this.blockFactory = blockFactory; + this.values = new double[size]; + } + + @Override + public DoubleVectorFixedBuilder appendDouble(double value) { + values[nextIndex++] = value; + return this; + } + + private static long ramBytesUsed(int size) { + return size == 1 + ? ConstantDoubleVector.RAM_BYTES_USED + : DoubleArrayVector.BASE_RAM_BYTES_USED + RamUsageEstimator.alignObjectSize( + (long) RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + size * Double.BYTES + ); + } + + @Override + public DoubleVector build() { + if (nextIndex < 0) { + throw new IllegalStateException("already closed"); + } + if (nextIndex != values.length) { + throw new IllegalStateException("expected to write [" + values.length + "] entries but wrote [" + nextIndex + "]"); + } + nextIndex = -1; + if (values.length == 1) { + return new ConstantDoubleVector(values[0], 1, blockFactory); + } + return new DoubleArrayVector(values, values.length, blockFactory); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java index 9f39c74c2e2a3..872b3790f045b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayVector.java @@ -17,7 +17,7 @@ */ public final class IntArrayVector extends AbstractVector implements IntVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(IntArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(IntArrayVector.class); private final int[] values; diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java index a6347cb5da70f..51497fc16d66e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java @@ -106,10 +106,22 @@ static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} + * if you know the size up front because it's faster. + */ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newIntVectorBuilder(estimatedSize); } + /** + * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} + * if you know the size up front because it's faster. + */ + static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { + return blockFactory.newIntVectorFixedBuilder(size); + } + /** Create a vector for a range of ints. */ static IntVector range(int startInclusive, int endExclusive) { int[] values = new int[endExclusive - startInclusive]; @@ -119,6 +131,9 @@ static IntVector range(int startInclusive, int endExclusive) { return new IntArrayVector(values, values.length); } + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits IntVectorBuilder { /** * Appends a int to the current entry. @@ -128,4 +143,17 @@ sealed interface Builder extends Vector.Builder permits IntVectorBuilder { @Override IntVector build(); } + + /** + * A builder that never grows. + */ + sealed interface FixedBuilder extends Vector.Builder permits IntVectorFixedBuilder { + /** + * Appends a int to the current entry. + */ + FixedBuilder appendInt(int value); + + @Override + IntVector build(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java index 8bf4a4a96c5cb..0533d5463a4e7 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java @@ -10,7 +10,7 @@ import java.util.Arrays; /** - * Block build of IntBlocks. + * Builder for {@link IntVector}s that grows as needed. * This class is generated. Do not edit it. */ final class IntVectorBuilder extends AbstractVectorBuilder implements IntVector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java new file mode 100644 index 0000000000000..19303b4024869 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.RamUsageEstimator; + +/** + * Builder for {@link IntVector}s that never grows. Prefer this to + * {@link IntVectorBuilder} if you know the precise size up front because + * it's faster. + * This class is generated. Do not edit it. + */ +final class IntVectorFixedBuilder implements IntVector.FixedBuilder { + private final BlockFactory blockFactory; + private final int[] values; + /** + * The next value to write into. {@code -1} means the vector has already + * been built. + */ + private int nextIndex; + + IntVectorFixedBuilder(int size, BlockFactory blockFactory) { + blockFactory.adjustBreaker(ramBytesUsed(size), false); + this.blockFactory = blockFactory; + this.values = new int[size]; + } + + @Override + public IntVectorFixedBuilder appendInt(int value) { + values[nextIndex++] = value; + return this; + } + + private static long ramBytesUsed(int size) { + return size == 1 + ? ConstantIntVector.RAM_BYTES_USED + : IntArrayVector.BASE_RAM_BYTES_USED + RamUsageEstimator.alignObjectSize( + (long) RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + size * Integer.BYTES + ); + } + + @Override + public IntVector build() { + if (nextIndex < 0) { + throw new IllegalStateException("already closed"); + } + if (nextIndex != values.length) { + throw new IllegalStateException("expected to write [" + values.length + "] entries but wrote [" + nextIndex + "]"); + } + nextIndex = -1; + if (values.length == 1) { + return new ConstantIntVector(values[0], 1, blockFactory); + } + return new IntArrayVector(values, values.length, blockFactory); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java index b4d467d44af3e..862a165761e84 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayVector.java @@ -17,7 +17,7 @@ */ public final class LongArrayVector extends AbstractVector implements LongVector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(LongArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(LongArrayVector.class); private final long[] values; diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java index 6e447dde251a4..584e9ecfa9ce0 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java @@ -107,10 +107,25 @@ static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} + * if you know the size up front because it's faster. + */ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newLongVectorBuilder(estimatedSize); } + /** + * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} + * if you know the size up front because it's faster. + */ + static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { + return blockFactory.newLongVectorFixedBuilder(size); + } + + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits LongVectorBuilder { /** * Appends a long to the current entry. @@ -120,4 +135,17 @@ sealed interface Builder extends Vector.Builder permits LongVectorBuilder { @Override LongVector build(); } + + /** + * A builder that never grows. + */ + sealed interface FixedBuilder extends Vector.Builder permits LongVectorFixedBuilder { + /** + * Appends a long to the current entry. + */ + FixedBuilder appendLong(long value); + + @Override + LongVector build(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java index 10daed94a966e..6b2e9f1de7d51 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java @@ -10,7 +10,7 @@ import java.util.Arrays; /** - * Block build of LongBlocks. + * Builder for {@link LongVector}s that grows as needed. * This class is generated. Do not edit it. */ final class LongVectorBuilder extends AbstractVectorBuilder implements LongVector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java new file mode 100644 index 0000000000000..5414b7669f588 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.RamUsageEstimator; + +/** + * Builder for {@link LongVector}s that never grows. Prefer this to + * {@link LongVectorBuilder} if you know the precise size up front because + * it's faster. + * This class is generated. Do not edit it. + */ +final class LongVectorFixedBuilder implements LongVector.FixedBuilder { + private final BlockFactory blockFactory; + private final long[] values; + /** + * The next value to write into. {@code -1} means the vector has already + * been built. + */ + private int nextIndex; + + LongVectorFixedBuilder(int size, BlockFactory blockFactory) { + blockFactory.adjustBreaker(ramBytesUsed(size), false); + this.blockFactory = blockFactory; + this.values = new long[size]; + } + + @Override + public LongVectorFixedBuilder appendLong(long value) { + values[nextIndex++] = value; + return this; + } + + private static long ramBytesUsed(int size) { + return size == 1 + ? ConstantLongVector.RAM_BYTES_USED + : LongArrayVector.BASE_RAM_BYTES_USED + RamUsageEstimator.alignObjectSize( + (long) RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + size * Long.BYTES + ); + } + + @Override + public LongVector build() { + if (nextIndex < 0) { + throw new IllegalStateException("already closed"); + } + if (nextIndex != values.length) { + throw new IllegalStateException("expected to write [" + values.length + "] entries but wrote [" + nextIndex + "]"); + } + nextIndex = -1; + if (values.length == 1) { + return new ConstantLongVector(values[0], 1, blockFactory); + } + return new LongArrayVector(values, values.length, blockFactory); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java index ca2c4e7c453d0..2afea228a4a78 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java @@ -46,7 +46,7 @@ private static BlockFactory loadGlobalFactory() { private final BigArrays bigArrays; - private BlockFactory(CircuitBreaker breaker, BigArrays bigArrays) { + public BlockFactory(CircuitBreaker breaker, BigArrays bigArrays) { this.breaker = breaker; this.bigArrays = bigArrays; } @@ -134,6 +134,10 @@ public BooleanBlock.Builder newBooleanBlockBuilder(int estimatedSize) { return new BooleanBlockBuilder(estimatedSize, this); } + BooleanVector.FixedBuilder newBooleanVectorFixedBuilder(int size) { + return new BooleanVectorFixedBuilder(size, this); + } + public BooleanBlock newBooleanArrayBlock( boolean[] values, int positionCount, @@ -180,6 +184,10 @@ public IntVector.Builder newIntVectorBuilder(int estimatedSize) { return new IntVectorBuilder(estimatedSize, this); } + IntVector.FixedBuilder newIntVectorFixedBuilder(int size) { + return new IntVectorFixedBuilder(size, this); + } + /** * Creates a new Vector with the given values and positionCount. Equivalent to: * newIntArrayVector(values, positionCount, 0L); // with zero pre-adjusted bytes @@ -225,6 +233,10 @@ public LongVector.Builder newLongVectorBuilder(int estimatedSize) { return new LongVectorBuilder(estimatedSize, this); } + LongVector.FixedBuilder newLongVectorFixedBuilder(int size) { + return new LongVectorFixedBuilder(size, this); + } + public LongVector newLongArrayVector(long[] values, int positionCount) { return newLongArrayVector(values, positionCount, 0L); } @@ -261,6 +273,10 @@ public DoubleVector.Builder newDoubleVectorBuilder(int estimatedSize) { return new DoubleVectorBuilder(estimatedSize, this); } + DoubleVector.FixedBuilder newDoubleVectorFixedBuilder(int size) { + return new DoubleVectorFixedBuilder(size, this); + } + public DoubleVector newDoubleArrayVector(double[] values, int positionCount) { return newDoubleArrayVector(values, positionCount, 0L); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st index 6065e95daaae9..b6a8714f882ee 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st @@ -25,7 +25,7 @@ $endif$ */ public final class $Type$ArrayVector extends AbstractVector implements $Type$Vector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance($Type$ArrayVector.class); + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance($Type$ArrayVector.class); $if(BytesRef)$ private final BytesRefArray values; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st index d1b724b37cd83..fd568cc17fc33 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ConstantVector.java.st @@ -18,7 +18,12 @@ import org.apache.lucene.util.RamUsageEstimator; */ public final class Constant$Type$Vector extends AbstractVector implements $Type$Vector { - private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(Constant$Type$Vector.class); +$if(BytesRef)$ + static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantBytesRefVector.class) + RamUsageEstimator + .shallowSizeOfInstance(BytesRef.class); +$else$ + static final long RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(Constant$Type$Vector.class); +$endif$ private final $type$ value; @@ -62,7 +67,11 @@ $endif$ @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + RamUsageEstimator.shallowSizeOfInstance($type$.class); +$if(BytesRef)$ + return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(value.bytes); +$else$ + return RAM_BYTES_USED; +$endif$ } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st index 4acb4243c131a..8be7024757a99 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st @@ -148,10 +148,31 @@ $endif$ return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } +$if(BytesRef)$ + /** + * Creates a builder that grows as needed. + */ +$else$ + /** + * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} + * if you know the size up front because it's faster. + */ +$endif$ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.new$Type$VectorBuilder(estimatedSize); } +$if(BytesRef)$ +$else$ + /** + * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} + * if you know the size up front because it's faster. + */ + static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { + return blockFactory.new$Type$VectorFixedBuilder(size); + } +$endif$ + $if(int)$ /** Create a vector for a range of ints. */ static IntVector range(int startInclusive, int endExclusive) { @@ -163,6 +184,9 @@ $if(int)$ } $endif$ + /** + * A builder that grows as needed. + */ sealed interface Builder extends Vector.Builder permits $Type$VectorBuilder { /** * Appends a $type$ to the current entry. @@ -172,4 +196,20 @@ $endif$ @Override $Type$Vector build(); } + +$if(BytesRef)$ +$else$ + /** + * A builder that never grows. + */ + sealed interface FixedBuilder extends Vector.Builder permits $Type$VectorFixedBuilder { + /** + * Appends a $type$ to the current entry. + */ + FixedBuilder append$Type$($type$ value); + + @Override + $Type$Vector build(); + } +$endif$ } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st index 09e95e16c303d..b813120b42e43 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st @@ -18,7 +18,7 @@ import java.util.Arrays; $endif$ /** - * Block build of $Type$Blocks. + * Builder for {@link $Type$Vector}s that grows as needed. * This class is generated. Do not edit it. */ final class $Type$VectorBuilder extends AbstractVectorBuilder implements $Type$Vector.Builder { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st new file mode 100644 index 0000000000000..86bc6b0a095d6 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.RamUsageEstimator; + +/** + * Builder for {@link $Type$Vector}s that never grows. Prefer this to + * {@link $Type$VectorBuilder} if you know the precise size up front because + * it's faster. + * This class is generated. Do not edit it. + */ +final class $Type$VectorFixedBuilder implements $Type$Vector.FixedBuilder { + private final BlockFactory blockFactory; + private final $type$[] values; + /** + * The next value to write into. {@code -1} means the vector has already + * been built. + */ + private int nextIndex; + + $Type$VectorFixedBuilder(int size, BlockFactory blockFactory) { + blockFactory.adjustBreaker(ramBytesUsed(size), false); + this.blockFactory = blockFactory; + this.values = new $type$[size]; + } + + @Override + public $Type$VectorFixedBuilder append$Type$($type$ value) { + values[nextIndex++] = value; + return this; + } + + private static long ramBytesUsed(int size) { + return size == 1 + ? Constant$Type$Vector.RAM_BYTES_USED + : $Type$ArrayVector.BASE_RAM_BYTES_USED + RamUsageEstimator.alignObjectSize( + (long) RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + size * $BYTES$ + ); + } + + @Override + public $Type$Vector build() { + if (nextIndex < 0) { + throw new IllegalStateException("already closed"); + } + if (nextIndex != values.length) { + throw new IllegalStateException("expected to write [" + values.length + "] entries but wrote [" + nextIndex + "]"); + } + nextIndex = -1; + if (values.length == 1) { + return new Constant$Type$Vector(values[0], 1, blockFactory); + } + return new $Type$ArrayVector(values, values.length, blockFactory); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java index e2a2f0b08452a..b21671cd30517 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/DriverContext.java @@ -61,7 +61,7 @@ public BigArrays bigArrays() { * The {@link CircuitBreaker} to use to track memory. */ public CircuitBreaker breaker() { - return bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + return blockFactory.breaker(); } public BlockFactory blockFactory() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java index f6ecdcec9c8df..a524221dd50d7 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -38,6 +38,10 @@ // BlockFactory is used and effectively tested in many other places, but this class contains tests // more specific to the factory implementation itself (and not necessarily tested elsewhere). public class BlockFactoryTests extends ESTestCase { + public static BlockFactory blockFactory(ByteSizeValue size) { + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, size).withCircuitBreaking(); + return new BlockFactory(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST), bigArrays); + } final CircuitBreaker breaker; final BigArrays bigArrays; diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java new file mode 100644 index 0000000000000..9fa9f7e32c654 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java @@ -0,0 +1,136 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.indices.CrankyCircuitBreakerService; +import org.elasticsearch.test.ESTestCase; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + +public class VectorFixedBuilderTests extends ESTestCase { + @ParametersFactory + public static List params() { + List params = new ArrayList<>(); + for (ElementType elementType : ElementType.values()) { + if (elementType == ElementType.UNKNOWN + || elementType == ElementType.NULL + || elementType == ElementType.DOC + || elementType == ElementType.BYTES_REF) { + continue; + } + params.add(new Object[] { elementType }); + } + return params; + } + + private final ElementType elementType; + + public VectorFixedBuilderTests(ElementType elementType) { + this.elementType = elementType; + } + + public void testBuildSmall() { + testBuild(between(1, 100)); + } + + public void testBuildHuge() { + testBuild(between(1_000, 50_000)); + } + + public void testBuildSingle() { + testBuild(1); + } + + private void testBuild(int size) { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + Vector.Builder builder = vectorBuilder(size, blockFactory); + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, size, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + } + } + + public void testDoubleBuild() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + Vector.Builder builder = vectorBuilder(10, blockFactory); + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + } + Exception e = expectThrows(IllegalStateException.class, builder::build); + assertThat(e.getMessage(), equalTo("already closed")); + } + + public void testCranky() { + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new CrankyCircuitBreakerService()); + BlockFactory blockFactory = new BlockFactory(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST), bigArrays); + try { + Vector.Builder builder = vectorBuilder(10, blockFactory); + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + } + // If we made it this far cranky didn't fail us! + } catch (CircuitBreakingException e) { + logger.info("cranky", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + private Vector.Builder vectorBuilder(int size, BlockFactory blockFactory) { + return switch (elementType) { + case NULL, BYTES_REF, DOC, UNKNOWN -> throw new UnsupportedOperationException(); + case BOOLEAN -> BooleanVector.newVectorFixedBuilder(size, blockFactory); + case DOUBLE -> DoubleVector.newVectorFixedBuilder(size, blockFactory); + case INT -> IntVector.newVectorFixedBuilder(size, blockFactory); + case LONG -> LongVector.newVectorFixedBuilder(size, blockFactory); + }; + } + + private void fill(Vector.Builder builder, Vector from) { + switch (elementType) { + case NULL, DOC, UNKNOWN -> throw new UnsupportedOperationException(); + case BOOLEAN -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((BooleanVector.FixedBuilder) builder).appendBoolean(((BooleanVector) from).getBoolean(p)); + } + } + case DOUBLE -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((DoubleVector.FixedBuilder) builder).appendDouble(((DoubleVector) from).getDouble(p)); + } + } + case INT -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((IntVector.FixedBuilder) builder).appendInt(((IntVector) from).getInt(p)); + } + } + case LONG -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((LongVector.FixedBuilder) builder).appendLong(((LongVector) from).getLong(p)); + } + } + } + } +} diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java index 13400890c4e47..19893bf8072f1 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java @@ -47,11 +47,13 @@ public class EsqlDisruptionIT extends EsqlActionIT { @Override protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { - return Settings.builder() + Settings settings = Settings.builder() .put(super.nodeSettings(nodeOrdinal, otherSettings)) .put(DEFAULT_SETTINGS) .put(ExchangeService.INACTIVE_SINKS_INTERVAL_SETTING, TimeValue.timeValueMillis(between(1000, 2000))) .build(); + logger.info("settings {}", settings); + return settings; } @Override diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java index 695844228c9ea..6eb2c1082d2de 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -38,7 +38,7 @@ public String name() { public Block evalNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -76,8 +76,8 @@ public Vector evalNotNullable(Block fieldVal) { MvAvg.process(work, value); } double result = MvAvg.finish(work, valueCount); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java index e85741f4ec501..27c1d50cdf400 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; @@ -39,7 +39,7 @@ public String name() { public Block evalNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -72,7 +72,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -80,7 +80,7 @@ public Vector evalNotNullable(Block fieldVal) { if (valueCount == 1) { int value = v.getInt(first); double result = MvAvg.single(value); - values[p] = result; + builder.appendDouble(result); continue; } int end = first + valueCount; @@ -89,9 +89,9 @@ public Vector evalNotNullable(Block fieldVal) { MvAvg.process(work, value); } double result = MvAvg.finish(work, valueCount); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } /** @@ -101,7 +101,7 @@ public Vector evalNotNullable(Block fieldVal) { public Block evalSingleValuedNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -125,7 +125,7 @@ public Block evalSingleValuedNullable(Block fieldVal) { public Vector evalSingleValuedNotNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -133,8 +133,8 @@ public Vector evalSingleValuedNotNullable(Block fieldVal) { int first = v.getFirstValueIndex(p); int value = v.getInt(first); double result = MvAvg.single(value); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java index b96096b8fe22e..652d7f73a0872 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; @@ -39,7 +39,7 @@ public String name() { public Block evalNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -72,7 +72,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -80,7 +80,7 @@ public Vector evalNotNullable(Block fieldVal) { if (valueCount == 1) { long value = v.getLong(first); double result = MvAvg.single(value); - values[p] = result; + builder.appendDouble(result); continue; } int end = first + valueCount; @@ -89,9 +89,9 @@ public Vector evalNotNullable(Block fieldVal) { MvAvg.process(work, value); } double result = MvAvg.finish(work, valueCount); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } /** @@ -101,7 +101,7 @@ public Vector evalNotNullable(Block fieldVal) { public Block evalSingleValuedNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -125,7 +125,7 @@ public Block evalSingleValuedNullable(Block fieldVal) { public Vector evalSingleValuedNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -133,8 +133,8 @@ public Vector evalSingleValuedNotNullable(Block fieldVal) { int first = v.getFirstValueIndex(p); long value = v.getLong(first); double result = MvAvg.single(value); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java index b0e0864c7017c..fd9f25124898b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; @@ -40,7 +40,7 @@ public String name() { public Block evalNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -73,7 +73,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -81,7 +81,7 @@ public Vector evalNotNullable(Block fieldVal) { if (valueCount == 1) { long value = v.getLong(first); double result = MvAvg.singleUnsignedLong(value); - values[p] = result; + builder.appendDouble(result); continue; } int end = first + valueCount; @@ -90,9 +90,9 @@ public Vector evalNotNullable(Block fieldVal) { MvAvg.processUnsignedLong(work, value); } double result = MvAvg.finish(work, valueCount); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } /** @@ -102,7 +102,7 @@ public Vector evalNotNullable(Block fieldVal) { public Block evalSingleValuedNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -126,7 +126,7 @@ public Block evalSingleValuedNullable(Block fieldVal) { public Vector evalSingleValuedNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -134,8 +134,8 @@ public Vector evalSingleValuedNotNullable(Block fieldVal) { int first = v.getFirstValueIndex(p); long value = v.getLong(first); double result = MvAvg.singleUnsignedLong(value); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java index 9ab7e6919d68e..3b15fe9f17293 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; +import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -41,7 +41,7 @@ public Block evalNullable(Block fieldVal) { } BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -71,7 +71,7 @@ public Vector evalNotNullable(Block fieldVal) { } BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - boolean[] values = new boolean[positionCount]; + BooleanVector.FixedBuilder builder = BooleanVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -82,9 +82,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMax.process(value, next); } boolean result = value; - values[p] = result; + builder.appendBoolean(result); } - return new BooleanArrayVector(values, positionCount); + return builder.build(); } /** @@ -93,7 +93,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -114,14 +114,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - boolean[] values = new boolean[positionCount]; + BooleanVector.FixedBuilder builder = BooleanVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMax.ascendingIndex(valueCount); boolean result = v.getBoolean(first + idx); - values[p] = result; + builder.appendBoolean(result); } - return new BooleanArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java index 5101d380c447e..6401664c9aa0d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java @@ -7,11 +7,9 @@ import java.lang.Override; import java.lang.String; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -44,7 +42,7 @@ public Block evalNullable(Block fieldVal) { } BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -76,7 +74,7 @@ public Vector evalNotNullable(Block fieldVal) { } BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVector.Builder builder = BytesRefVector.newVectorBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -89,9 +87,9 @@ public Vector evalNotNullable(Block fieldVal) { MvMax.process(value, next); } BytesRef result = value; - values.append(result); + builder.appendBytesRef(result); } - return new BytesRefArrayVector(values, positionCount); + return builder.build(); } /** @@ -100,7 +98,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -123,7 +121,7 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVector.Builder builder = BytesRefVector.newVectorBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -131,8 +129,8 @@ private Vector evalAscendingNotNullable(Block fieldVal) { int first = v.getFirstValueIndex(p); int idx = MvMax.ascendingIndex(valueCount); BytesRef result = v.getBytesRef(first + idx, firstScratch); - values.append(result); + builder.appendBytesRef(result); } - return new BytesRefArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java index 4898a2ddac0ab..0ec72b82e2438 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMax.process(value, next); } double result = value; - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMax.ascendingIndex(valueCount); double result = v.getDouble(first + idx); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java index fdeb4c4c5a469..2bf14b26c6c5e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMax.process(value, next); } int result = value; - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMax.ascendingIndex(valueCount); int result = v.getInt(first + idx); - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java index 99e79f59160a6..ce5a95bee7699 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMax.process(value, next); } long result = value; - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMax.ascendingIndex(valueCount); long result = v.getLong(first + idx); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java index ab2422df3b7b1..15d47db18751f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -38,7 +38,7 @@ public String name() { public Block evalNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Doubles work = new MvMedian.Doubles(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Doubles work = new MvMedian.Doubles(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -76,8 +76,8 @@ public Vector evalNotNullable(Block fieldVal) { MvMedian.process(work, value); } double result = MvMedian.finish(work); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java index f66e1b65ed131..711992a20763e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Ints work = new MvMedian.Ints(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Ints work = new MvMedian.Ints(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { MvMedian.process(work, value); } int result = MvMedian.finish(work); - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Ints work = new MvMedian.Ints(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Ints work = new MvMedian.Ints(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int result = MvMedian.ascending(v, first, valueCount); - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java index 7ced56110af42..67d3c123a6953 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -41,7 +41,7 @@ public Block evalNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -71,7 +71,7 @@ public Vector evalNotNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -82,9 +82,9 @@ public Vector evalNotNullable(Block fieldVal) { MvMedian.process(work, value); } long result = MvMedian.finish(work); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } /** @@ -93,7 +93,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -114,14 +114,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); long result = MvMedian.ascending(v, first, valueCount); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java index 162608e055374..93538708039b5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -41,7 +41,7 @@ public Block evalNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -71,7 +71,7 @@ public Vector evalNotNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -82,9 +82,9 @@ public Vector evalNotNullable(Block fieldVal) { MvMedian.processUnsignedLong(work, value); } long result = MvMedian.finishUnsignedLong(work); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } /** @@ -93,7 +93,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -114,14 +114,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); MvMedian.Longs work = new MvMedian.Longs(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); long result = MvMedian.ascendingUnsignedLong(v, first, valueCount); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java index 3a67c042d82f0..6e16c8db4b896 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; +import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -41,7 +41,7 @@ public Block evalNullable(Block fieldVal) { } BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -71,7 +71,7 @@ public Vector evalNotNullable(Block fieldVal) { } BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - boolean[] values = new boolean[positionCount]; + BooleanVector.FixedBuilder builder = BooleanVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -82,9 +82,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMin.process(value, next); } boolean result = value; - values[p] = result; + builder.appendBoolean(result); } - return new BooleanArrayVector(values, positionCount); + return builder.build(); } /** @@ -93,7 +93,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -114,14 +114,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { BooleanBlock v = (BooleanBlock) fieldVal; int positionCount = v.getPositionCount(); - boolean[] values = new boolean[positionCount]; + BooleanVector.FixedBuilder builder = BooleanVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMin.ascendingIndex(valueCount); boolean result = v.getBoolean(first + idx); - values[p] = result; + builder.appendBoolean(result); } - return new BooleanArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java index 538cf9dbed307..99a671cf0a2df 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java @@ -7,11 +7,9 @@ import java.lang.Override; import java.lang.String; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -44,7 +42,7 @@ public Block evalNullable(Block fieldVal) { } BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -76,7 +74,7 @@ public Vector evalNotNullable(Block fieldVal) { } BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVector.Builder builder = BytesRefVector.newVectorBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -89,9 +87,9 @@ public Vector evalNotNullable(Block fieldVal) { MvMin.process(value, next); } BytesRef result = value; - values.append(result); + builder.appendBytesRef(result); } - return new BytesRefArrayVector(values, positionCount); + return builder.build(); } /** @@ -100,7 +98,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -123,7 +121,7 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { BytesRefBlock v = (BytesRefBlock) fieldVal; int positionCount = v.getPositionCount(); - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefVector.Builder builder = BytesRefVector.newVectorBuilder(positionCount, driverContext.blockFactory()); BytesRef firstScratch = new BytesRef(); BytesRef nextScratch = new BytesRef(); for (int p = 0; p < positionCount; p++) { @@ -131,8 +129,8 @@ private Vector evalAscendingNotNullable(Block fieldVal) { int first = v.getFirstValueIndex(p); int idx = MvMin.ascendingIndex(valueCount); BytesRef result = v.getBytesRef(first + idx, firstScratch); - values.append(result); + builder.appendBytesRef(result); } - return new BytesRefArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java index 905c6dc87eaf1..e40ff78d0d364 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMin.process(value, next); } double result = value; - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMin.ascendingIndex(valueCount); double result = v.getDouble(first + idx); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java index f71ea2a663314..9412930da53c5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMin.process(value, next); } int result = value; - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - int[] values = new int[positionCount]; + IntVector.FixedBuilder builder = IntVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMin.ascendingIndex(valueCount); int result = v.getInt(first + idx); - values[p] = result; + builder.appendInt(result); } - return new IntArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java index da44e992c266f..1fac131f0de0c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -40,7 +40,7 @@ public Block evalNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -70,7 +70,7 @@ public Vector evalNotNullable(Block fieldVal) { } LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); @@ -81,9 +81,9 @@ public Vector evalNotNullable(Block fieldVal) { value = MvMin.process(value, next); } long result = value; - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } /** @@ -92,7 +92,7 @@ public Vector evalNotNullable(Block fieldVal) { private Block evalAscendingNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { @@ -113,14 +113,14 @@ private Block evalAscendingNullable(Block fieldVal) { private Vector evalAscendingNotNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - long[] values = new long[positionCount]; + LongVector.FixedBuilder builder = LongVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); int first = v.getFirstValueIndex(p); int idx = MvMin.ascendingIndex(valueCount); long result = v.getLong(first + idx); - values[p] = result; + builder.appendLong(result); } - return new LongArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java index 4b7903ae2b4c2..207706d5dd9f9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java @@ -7,8 +7,8 @@ import java.lang.Override; import java.lang.String; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; @@ -38,7 +38,7 @@ public String name() { public Block evalNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { public Vector evalNotNullable(Block fieldVal) { DoubleBlock v = (DoubleBlock) fieldVal; int positionCount = v.getPositionCount(); - double[] values = new double[positionCount]; + DoubleVector.FixedBuilder builder = DoubleVector.newVectorFixedBuilder(positionCount, driverContext.blockFactory()); CompensatedSum work = new CompensatedSum(); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); @@ -76,8 +76,8 @@ public Vector evalNotNullable(Block fieldVal) { MvSum.process(work, value); } double result = MvSum.finish(work); - values[p] = result; + builder.appendDouble(result); } - return new DoubleArrayVector(values, positionCount); + return builder.build(); } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java index da7abdfd1efd4..98e5036c9be93 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java @@ -42,7 +42,7 @@ public String name() { public Block evalNullable(Block fieldVal) { IntBlock v = (IntBlock) fieldVal; int positionCount = v.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java index f669e88e1aeba..9053a480a1355 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java @@ -42,7 +42,7 @@ public String name() { public Block evalNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java index 18773ea49f245..0354ba77edc26 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java @@ -42,7 +42,7 @@ public String name() { public Block evalNullable(Block fieldVal) { LongBlock v = (LongBlock) fieldVal; int positionCount = v.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = v.getValueCount(p); if (valueCount == 0) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java index e26b7891be570..b0fa12ec255bb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/mapper/EvaluatorMapper.java @@ -9,10 +9,12 @@ import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.breaker.NoopCircuitBreaker; +import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; -import org.elasticsearch.compute.operator.ThrowingDriverContext; import org.elasticsearch.xpack.ql.expression.Expression; import java.util.function.Function; @@ -41,12 +43,12 @@ public Block eval(Page page) { @Override public void close() {} - }).get(new ThrowingDriverContext() { - @Override - public CircuitBreaker breaker() { + }).get( + new DriverContext( + BigArrays.NON_RECYCLING_INSTANCE, // TODO maybe this should have a small fixed limit? - return new NoopCircuitBreaker(CircuitBreaker.REQUEST); - } - }).eval(new Page(1)), 0); + new BlockFactory(new NoopCircuitBreaker(CircuitBreaker.REQUEST), BigArrays.NON_RECYCLING_INSTANCE) + ) + ).eval(new Page(1)), 0); } } From d7eff41c1fc9a992c6cfcc0506d5b20bcb65c5ed Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 27 Sep 2023 15:38:23 -0400 Subject: [PATCH 108/155] ESQL: Clone blocks in CSV tests (#99976) Previously in our tests we loaded the data once and sent it to as many threads as we needed. The trouble with this is that we want to `close` the blocks after we've consumed them. If we do that then we can't share them! This sends unqiue blocks to each reader. --- .../esql/planner/TestPhysicalOperationProviders.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java index d10f58671d71c..b5ca653e2cfd9 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java @@ -291,11 +291,12 @@ private Block extractBlockForColumn(Page page, String columnName) { } DocBlock docBlock = page.getBlock(0); IntVector docIndices = docBlock.asVector().docs(); - Block loadedBlock = testData.getBlock(columnIndex); - int[] filteredPositions = new int[docIndices.getPositionCount()]; + Block originalData = testData.getBlock(columnIndex); + Block.Builder builder = originalData.elementType().newBlockBuilder(docIndices.getPositionCount()); for (int c = 0; c < docIndices.getPositionCount(); c++) { - filteredPositions[c] = docIndices.getInt(c); + int doc = docIndices.getInt(c); + builder.copyFrom(originalData, doc, doc + 1); } - return loadedBlock.filter(filteredPositions); + return builder.build(); } } From ae17505557a9951fb9c8a89b9c616b97a5a2c791 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 27 Sep 2023 12:45:39 -0700 Subject: [PATCH 109/155] Introduce authorization for enrich in ESQL (#99646) This change introduces a new privilege monitor_enrich. Users are required to have this privilege in order to use the enrich functionality in ESQL. Additionally, it eliminates the need to use the enrich_origin when executing enrich lookups. The enrich_origin will only be used when resolving enrich policies to prevent warnings when accessing system indices directly. Closes #98482 --- .../security/get-builtin-privileges.asciidoc | 1 + .../privilege/ClusterPrivilegeResolver.java | 6 +- .../authz/privilege/PrivilegeTests.java | 11 ++ x-pack/plugin/esql/qa/security/build.gradle | 2 + x-pack/plugin/esql/qa/security/roles.yml | 21 +++- .../xpack/esql/EsqlSecurityIT.java | 28 +++-- .../esql/enrich/EnrichLookupService.java | 25 ++--- .../esql/enrich/EnrichPolicyResolver.java | 103 +++++++++++++++--- .../xpack/esql/execution/PlanExecutor.java | 35 +++--- .../xpack/esql/plugin/EsqlPlugin.java | 9 +- .../esql/plugin/TransportEsqlQueryAction.java | 4 + .../esql/stats/PlanExecutorMetricsTests.java | 8 +- .../test/privileges/11_builtin.yml | 2 +- 13 files changed, 186 insertions(+), 69 deletions(-) diff --git a/docs/reference/rest-api/security/get-builtin-privileges.asciidoc b/docs/reference/rest-api/security/get-builtin-privileges.asciidoc index ce7263e4d46f3..8f75293e2c1a4 100644 --- a/docs/reference/rest-api/security/get-builtin-privileges.asciidoc +++ b/docs/reference/rest-api/security/get-builtin-privileges.asciidoc @@ -98,6 +98,7 @@ A successful call returns an object with "cluster" and "index" fields. "manage_watcher", "monitor", "monitor_data_frame_transforms", + "monitor_enrich", "monitor_ml", "monitor_rollup", "monitor_snapshot", diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 9edda8b61739b..68c8491b70528 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -101,6 +101,8 @@ public class ClusterPrivilegeResolver { private static final Set MONITOR_TRANSFORM_PATTERN = Set.of("cluster:monitor/data_frame/*", "cluster:monitor/transform/*"); private static final Set MONITOR_WATCHER_PATTERN = Set.of("cluster:monitor/xpack/watcher/*"); private static final Set MONITOR_ROLLUP_PATTERN = Set.of("cluster:monitor/xpack/rollup/*"); + private static final Set MONITOR_ENRICH_PATTERN = Set.of("cluster:monitor/xpack/enrich/*", "cluster:admin/xpack/enrich/get"); + private static final Set ALL_CLUSTER_PATTERN = Set.of( "cluster:*", "indices:admin/template/*", @@ -172,7 +174,7 @@ public class ClusterPrivilegeResolver { XPackInfoAction.NAME, ClusterStateAction.NAME ); - private static final Set MANAGE_ENRICH_AUTOMATON = Set.of("cluster:admin/xpack/enrich/*"); + private static final Set MANAGE_ENRICH_AUTOMATON = Set.of("cluster:admin/xpack/enrich/*", "cluster:monitor/xpack/enrich/*"); public static final NamedClusterPrivilege NONE = new ActionClusterPrivilege("none", Set.of(), Set.of()); public static final NamedClusterPrivilege ALL = new ActionClusterPrivilege("all", ALL_CLUSTER_PATTERN); @@ -192,6 +194,7 @@ public class ClusterPrivilegeResolver { ); public static final NamedClusterPrivilege MONITOR_WATCHER = new ActionClusterPrivilege("monitor_watcher", MONITOR_WATCHER_PATTERN); public static final NamedClusterPrivilege MONITOR_ROLLUP = new ActionClusterPrivilege("monitor_rollup", MONITOR_ROLLUP_PATTERN); + public static final NamedClusterPrivilege MONITOR_ENRICH = new ActionClusterPrivilege("monitor_enrich", MONITOR_ENRICH_PATTERN); public static final NamedClusterPrivilege MANAGE = new ActionClusterPrivilege("manage", ALL_CLUSTER_PATTERN, ALL_SECURITY_PATTERN); public static final NamedClusterPrivilege MANAGE_ML = new ActionClusterPrivilege("manage_ml", MANAGE_ML_PATTERN); public static final NamedClusterPrivilege MANAGE_TRANSFORM_DEPRECATED = new ActionClusterPrivilege( @@ -333,6 +336,7 @@ public class ClusterPrivilegeResolver { MONITOR_TRANSFORM, MONITOR_WATCHER, MONITOR_ROLLUP, + MONITOR_ENRICH, MANAGE, MANAGE_ML, MANAGE_TRANSFORM_DEPRECATED, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java index 1ba478d61943e..59add1cac3539 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java @@ -379,9 +379,20 @@ public void testManageEnrichPrivilege() { verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_ENRICH, GetEnrichPolicyAction.NAME); verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_ENRICH, PutEnrichPolicyAction.NAME); verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_ENRICH, "cluster:admin/xpack/enrich/brand_new_api"); + verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_ENRICH, "cluster:monitor/xpack/enrich/esql/resolve_policy"); verifyClusterActionDenied(ClusterPrivilegeResolver.MANAGE_ENRICH, "cluster:admin/xpack/whatever"); } + public void testMonitorEnrichPrivilege() { + verifyClusterActionAllowed(ClusterPrivilegeResolver.MONITOR_ENRICH, "cluster:monitor/xpack/enrich/esql/resolve_policy"); + verifyClusterActionAllowed(ClusterPrivilegeResolver.MONITOR_ENRICH, GetEnrichPolicyAction.NAME); + verifyClusterActionAllowed(ClusterPrivilegeResolver.MONITOR_ENRICH, "cluster:monitor/xpack/enrich/brand_new_api"); + verifyClusterActionDenied(ClusterPrivilegeResolver.MONITOR_ENRICH, PutEnrichPolicyAction.NAME); + verifyClusterActionDenied(ClusterPrivilegeResolver.MONITOR_ENRICH, ExecuteEnrichPolicyAction.NAME); + verifyClusterActionDenied(ClusterPrivilegeResolver.MONITOR_ENRICH, DeleteEnrichPolicyAction.NAME); + verifyClusterActionDenied(ClusterPrivilegeResolver.MONITOR_ENRICH, "cluster:admin/xpack/whatever"); + } + public void testIlmPrivileges() { { verifyClusterActionAllowed( diff --git a/x-pack/plugin/esql/qa/security/build.gradle b/x-pack/plugin/esql/qa/security/build.gradle index 4a1b32587da61..44a4f5a27efea 100644 --- a/x-pack/plugin/esql/qa/security/build.gradle +++ b/x-pack/plugin/esql/qa/security/build.gradle @@ -11,4 +11,6 @@ testClusters.configureEach { user username: "user1", password: 'x-pack-test-password', role: "user1" user username: "user2", password: 'x-pack-test-password', role: "user2" user username: "user3", password: 'x-pack-test-password', role: "user3" + user username: "user4", password: 'x-pack-test-password', role: "user4" + user username: "user5", password: 'x-pack-test-password', role: "user5" } diff --git a/x-pack/plugin/esql/qa/security/roles.yml b/x-pack/plugin/esql/qa/security/roles.yml index d18389dc58879..7a89fa57f7102 100644 --- a/x-pack/plugin/esql/qa/security/roles.yml +++ b/x-pack/plugin/esql/qa/security/roles.yml @@ -13,6 +13,7 @@ test-admin: user1: cluster: - cluster:monitor/main + - manage_enrich indices: - names: ['index-user1', 'index', "test-enrich" ] privileges: @@ -22,8 +23,7 @@ user1: - indices:admin/refresh user2: - cluster: - - cluster:monitor/main + cluster: [] indices: - names: [ 'index-user2', 'index' ] privileges: @@ -33,8 +33,7 @@ user2: - indices:admin/refresh user3: - cluster: - - cluster:monitor/main + cluster: [] indices: - names: [ 'index' ] privileges: [ 'read' ] @@ -44,3 +43,17 @@ user3: "org": "sales" } } + +user4: + cluster: + - monitor_enrich + indices: + - names: ['index-user1', 'index', "test-enrich" ] + privileges: + - read +user5: + cluster: [] + indices: + - names: ['index-user1', 'index', "test-enrich" ] + privileges: + - read diff --git a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java index 3d637e30da4c1..3d0787048685c 100644 --- a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java +++ b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.esql; +import org.apache.http.HttpStatus; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; @@ -152,15 +153,26 @@ record Listen(long timestamp, String songId, double duration) { client().performRequest(indexDoc); } refresh("test-enrich"); - Response resp = runESQLCommand( - "user1", - "FROM test-enrich | ENRICH songs ON song_id | stats total_duration = sum(duration) by artist | sort artist" - ); - Map respMap = entityAsMap(resp); - assertThat( - respMap.get("values"), - equalTo(List.of(List.of(2.75, "Disturbed"), List.of(10.5, "Eagles"), List.of(8.25, "Linkin Park"))) + for (String user : List.of("user1", "user4")) { + Response resp = runESQLCommand( + user, + "FROM test-enrich | ENRICH songs ON song_id | stats total_duration = sum(duration) by artist | sort artist" + ); + Map respMap = entityAsMap(resp); + assertThat( + respMap.get("values"), + equalTo(List.of(List.of(2.75, "Disturbed"), List.of(10.5, "Eagles"), List.of(8.25, "Linkin Park"))) + ); + } + + ResponseException resp = expectThrows( + ResponseException.class, + () -> runESQLCommand( + "user5", + "FROM test-enrich | ENRICH songs ON song_id | stats total_duration = sum(duration) by artist | sort artist" + ) ); + assertThat(resp.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); } finally { removeEnrichPolicy(); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index ba07a688387cc..ca37b498f05ac 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -12,7 +12,6 @@ import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.UnavailableShardsException; import org.elasticsearch.action.support.ChannelActionListener; -import org.elasticsearch.action.support.ContextPreservingActionListener; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -23,7 +22,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; @@ -52,7 +50,6 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.action.EsqlQueryAction; import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry; @@ -141,19 +138,15 @@ public void lookupAsync( } DiscoveryNode targetNode = clusterState.nodes().get(shardRouting.currentNodeId()); LookupRequest lookupRequest = new LookupRequest(sessionId, shardIt.shardId(), matchType, matchField, inputPage, extractFields); - ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); - listener = ContextPreservingActionListener.wrapPreservingContext(listener, threadContext); - try (ThreadContext.StoredContext ignored = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { - // TODO: handle retry and avoid forking for the local lookup - transportService.sendChildRequest( - targetNode, - LOOKUP_ACTION_NAME, - lookupRequest, - parentTask, - TransportRequestOptions.EMPTY, - new ActionListenerResponseHandler<>(listener.map(r -> r.page), LookupResponse::new, executor) - ); - } + // TODO: handle retry and avoid forking for the local lookup + transportService.sendChildRequest( + targetNode, + LOOKUP_ACTION_NAME, + lookupRequest, + parentTask, + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(listener.map(r -> r.page), LookupResponse::new, executor) + ); } private void doLookup( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java index a2b1d914d1435..cdafee6d76ef0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java @@ -8,46 +8,123 @@ package org.elasticsearch.xpack.esql.enrich; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionListenerResponseHandler; +import org.elasticsearch.action.support.ChannelActionListener; import org.elasticsearch.action.support.ContextPreservingActionListener; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportChannel; +import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.transport.TransportRequestHandler; +import org.elasticsearch.transport.TransportResponse; +import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.enrich.EnrichMetadata; import org.elasticsearch.xpack.core.enrich.EnrichPolicy; +import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.ql.index.IndexResolver; import java.util.Map; import java.util.Set; public class EnrichPolicyResolver { + private static final String RESOLVE_ACTION_NAME = "cluster:monitor/xpack/enrich/esql/resolve_policy"; private final ClusterService clusterService; private final IndexResolver indexResolver; + private final TransportService transportService; private final ThreadPool threadPool; - public EnrichPolicyResolver(ClusterService clusterService, IndexResolver indexResolver, ThreadPool threadPool) { + public EnrichPolicyResolver(ClusterService clusterService, TransportService transportService, IndexResolver indexResolver) { this.clusterService = clusterService; + this.transportService = transportService; this.indexResolver = indexResolver; - this.threadPool = threadPool; + this.threadPool = transportService.getThreadPool(); + transportService.registerRequestHandler( + RESOLVE_ACTION_NAME, + threadPool.executor(EsqlPlugin.ESQL_THREAD_POOL_NAME), + ResolveRequest::new, + new RequestHandler() + ); } public void resolvePolicy(String policyName, ActionListener listener) { - EnrichPolicy policy = policies().get(policyName); - ThreadContext threadContext = threadPool.getThreadContext(); - listener = ContextPreservingActionListener.wrapPreservingContext(listener, threadContext); - try (ThreadContext.StoredContext ignored = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { - indexResolver.resolveAsMergedMapping( - EnrichPolicy.getBaseName(policyName), - IndexResolver.ALL_FIELDS, - false, - Map.of(), - listener.map(indexResult -> new EnrichPolicyResolution(policyName, policy, indexResult)) - ); + transportService.sendRequest( + clusterService.localNode(), + RESOLVE_ACTION_NAME, + new ResolveRequest(policyName), + new ActionListenerResponseHandler<>( + listener.map(r -> r.resolution), + ResolveResponse::new, + threadPool.executor(EsqlPlugin.ESQL_THREAD_POOL_NAME) + ) + ); + } + + private static UnsupportedOperationException unsupported() { + return new UnsupportedOperationException("local node transport action"); + } + + private static class ResolveRequest extends TransportRequest { + private final String policyName; + + ResolveRequest(String policyName) { + this.policyName = policyName; + } + + ResolveRequest(StreamInput in) { + throw unsupported(); + } + + @Override + public void writeTo(StreamOutput out) { + throw unsupported(); + } + } + + private static class ResolveResponse extends TransportResponse { + private final EnrichPolicyResolution resolution; + + ResolveResponse(EnrichPolicyResolution resolution) { + this.resolution = resolution; + } + + ResolveResponse(StreamInput in) { + throw unsupported(); + } + + @Override + public void writeTo(StreamOutput out) { + throw unsupported(); + } + } + + private class RequestHandler implements TransportRequestHandler { + @Override + public void messageReceived(ResolveRequest request, TransportChannel channel, Task task) throws Exception { + String policyName = request.policyName; + EnrichPolicy policy = policies().get(policyName); + ThreadContext threadContext = threadPool.getThreadContext(); + ActionListener listener = new ChannelActionListener<>(channel); + listener = ContextPreservingActionListener.wrapPreservingContext(listener, threadContext); + try (ThreadContext.StoredContext ignored = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { + indexResolver.resolveAsMergedMapping( + EnrichPolicy.getBaseName(policyName), + IndexResolver.ALL_FIELDS, + false, + Map.of(), + listener.map(indexResult -> new ResolveResponse(new EnrichPolicyResolution(policyName, policy, indexResult))) + ); + } } } public Set allPolicyNames() { + // TODO: remove this suggestion as it exposes policy names without the right permission return policies().keySet(); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java index 798927d2c9329..eaf148c27c3ee 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java @@ -28,7 +28,6 @@ public class PlanExecutor { private final IndexResolver indexResolver; - private final EnrichPolicyResolver enrichPolicyResolver; private final PreAnalyzer preAnalyzer; private final FunctionRegistry functionRegistry; private final LogicalPlanOptimizer logicalPlanOptimizer; @@ -36,9 +35,8 @@ public class PlanExecutor { private final Metrics metrics; private final Verifier verifier; - public PlanExecutor(IndexResolver indexResolver, EnrichPolicyResolver enrichPolicyResolver) { + public PlanExecutor(IndexResolver indexResolver) { this.indexResolver = indexResolver; - this.enrichPolicyResolver = enrichPolicyResolver; this.preAnalyzer = new PreAnalyzer(); this.functionRegistry = new EsqlFunctionRegistry(); this.logicalPlanOptimizer = new LogicalPlanOptimizer(); @@ -47,18 +45,14 @@ public PlanExecutor(IndexResolver indexResolver, EnrichPolicyResolver enrichPoli this.verifier = new Verifier(metrics); } - public void esql(EsqlQueryRequest request, String sessionId, EsqlConfiguration cfg, ActionListener listener) { - QueryMetric clientId = QueryMetric.fromString("rest"); - metrics.total(clientId); - newSession(sessionId, cfg).execute(request, wrap(listener::onResponse, ex -> { - // TODO when we decide if we will differentiate Kibana from REST, this String value will likely come from the request - metrics.failed(clientId); - listener.onFailure(ex); - })); - } - - private EsqlSession newSession(String sessionId, EsqlConfiguration cfg) { - return new EsqlSession( + public void esql( + EsqlQueryRequest request, + String sessionId, + EsqlConfiguration cfg, + EnrichPolicyResolver enrichPolicyResolver, + ActionListener listener + ) { + final var session = new EsqlSession( sessionId, cfg, indexResolver, @@ -69,6 +63,17 @@ private EsqlSession newSession(String sessionId, EsqlConfiguration cfg) { mapper, verifier ); + QueryMetric clientId = QueryMetric.fromString("rest"); + metrics.total(clientId); + session.execute(request, wrap(listener::onResponse, ex -> { + // TODO when we decide if we will differentiate Kibana from REST, this String value will likely come from the request + metrics.failed(clientId); + listener.onFailure(ex); + })); + } + + public IndexResolver indexResolver() { + return indexResolver; } public Metrics metrics() { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java index 909c6c12893ab..62a74e5023773 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java @@ -51,7 +51,6 @@ import org.elasticsearch.xpack.esql.EsqlUsageTransportAction; import org.elasticsearch.xpack.esql.action.EsqlQueryAction; import org.elasticsearch.xpack.esql.action.RestEsqlQueryAction; -import org.elasticsearch.xpack.esql.enrich.EnrichPolicyResolver; import org.elasticsearch.xpack.esql.execution.PlanExecutor; import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery; import org.elasticsearch.xpack.esql.type.EsqlDataTypeRegistry; @@ -101,14 +100,8 @@ public Collection createComponents( AllocationService allocationService, IndicesService indicesService ) { - IndexResolver indexResolver = new IndexResolver( - client, - clusterService.getClusterName().value(), - EsqlDataTypeRegistry.INSTANCE, - Set::of - ); return List.of( - new PlanExecutor(indexResolver, new EnrichPolicyResolver(clusterService, indexResolver, threadPool)), + new PlanExecutor(new IndexResolver(client, clusterService.getClusterName().value(), EsqlDataTypeRegistry.INSTANCE, Set::of)), new ExchangeService(clusterService.getSettings(), threadPool, EsqlPlugin.ESQL_THREAD_POOL_NAME) ); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java index edfae0f91297d..1ff00401029cf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/TransportEsqlQueryAction.java @@ -29,6 +29,7 @@ import org.elasticsearch.xpack.esql.action.EsqlQueryRequest; import org.elasticsearch.xpack.esql.action.EsqlQueryResponse; import org.elasticsearch.xpack.esql.enrich.EnrichLookupService; +import org.elasticsearch.xpack.esql.enrich.EnrichPolicyResolver; import org.elasticsearch.xpack.esql.execution.PlanExecutor; import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -45,6 +46,7 @@ public class TransportEsqlQueryAction extends HandledTransportAction computeService.execute( sessionId, diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/stats/PlanExecutorMetricsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/stats/PlanExecutorMetricsTests.java index 87aea66d1eeb7..95a6389af7fc5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/stats/PlanExecutorMetricsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/stats/PlanExecutorMetricsTests.java @@ -55,8 +55,10 @@ public void shutdownThreadPool() throws Exception { public void testFailedMetric() { Client client = mock(Client.class); IndexResolver idxResolver = new IndexResolver(client, randomAlphaOfLength(10), EsqlDataTypeRegistry.INSTANCE, Set::of); - var planExecutor = new PlanExecutor(idxResolver, new EnrichPolicyResolver(null, idxResolver, threadPool)); + var planExecutor = new PlanExecutor(idxResolver); String[] indices = new String[] { "test" }; + EnrichPolicyResolver enrichResolver = mock(EnrichPolicyResolver.class); + when(enrichResolver.allPolicyNames()).thenReturn(Set.of()); // simulate a valid field_caps response so we can parse and correctly analyze de query FieldCapabilitiesResponse fieldCapabilitiesResponse = mock(FieldCapabilitiesResponse.class); @@ -72,7 +74,7 @@ public void testFailedMetric() { var request = new EsqlQueryRequest(); // test a failed query: xyz field doesn't exist request.query("from test | stats m = max(xyz)"); - planExecutor.esql(request, randomAlphaOfLength(10), EsqlTestUtils.TEST_CFG, new ActionListener() { + planExecutor.esql(request, randomAlphaOfLength(10), EsqlTestUtils.TEST_CFG, enrichResolver, new ActionListener<>() { @Override public void onResponse(PhysicalPlan physicalPlan) { fail("this shouldn't happen"); @@ -91,7 +93,7 @@ public void onFailure(Exception e) { // fix the failing query: foo field does exist request.query("from test | stats m = max(foo)"); - planExecutor.esql(request, randomAlphaOfLength(10), EsqlTestUtils.TEST_CFG, new ActionListener() { + planExecutor.esql(request, randomAlphaOfLength(10), EsqlTestUtils.TEST_CFG, enrichResolver, new ActionListener<>() { @Override public void onResponse(PhysicalPlan physicalPlan) {} diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml index 8ec89e5a4c99c..e2e220aa55456 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml @@ -15,5 +15,5 @@ setup: # This is fragile - it needs to be updated every time we add a new cluster/index privilege # I would much prefer we could just check that specific entries are in the array, but we don't have # an assertion for that - - length: { "cluster" : 52 } + - length: { "cluster" : 53 } - length: { "index" : 22 } From 68867c783e05c3cc8df2752edd2e1052fd3f6e40 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 27 Sep 2023 21:24:09 -0700 Subject: [PATCH 110/155] Remove ownership hack in ExchangeResponse (#99891) This change removes the hack in ExchangeResponse. Now when an ExchangeSource receives a response, it takes ownership of the page instead of relying on serialization. Initially, I tried to use the exchange sink directly for the local connection; however, it still required a hack in the deserialization process (see b577af5). I think this approach is clearer in terms of ownership. --- .../operator/exchange/ExchangeResponse.java | 20 +++++++++---------- .../exchange/ExchangeSourceHandler.java | 2 +- .../exchange/ExchangeServiceTests.java | 7 ++++--- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java index ee5ac7f91c96e..9b3da39fe5c74 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeResponse.java @@ -23,13 +23,7 @@ public final class ExchangeResponse extends TransportResponse implements Releasa private final RefCounted counted = AbstractRefCounted.of(this::close); private final Page page; private final boolean finished; - /** - * We always use the remote exchange framwork even for local exchanges, but - * local exchanges shouldn't close the Page. Remote exchanges totally should - * as soon as they've serialized the block. This is a clever hack Nhat mentioned - * that can do that. But I don't like it. But it works and might unstick us. - */ - private boolean serialized = false; + private boolean pageTaken; public ExchangeResponse(Page page, boolean finished) { this.page = page; @@ -44,16 +38,20 @@ public ExchangeResponse(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { - serialized = true; out.writeOptionalWriteable(page); out.writeBoolean(finished); } /** - * Returns a page responded by {@link RemoteSink}. This can be null and out of order. + * Take the ownership of the page responded by {@link RemoteSink}. This can be null and out of order. */ @Nullable - public Page page() { + public Page takePage() { + if (pageTaken) { + assert false : "Page was taken already"; + throw new IllegalStateException("Page was taken already"); + } + pageTaken = true; return page; } @@ -100,7 +98,7 @@ public boolean hasReferences() { @Override public void close() { - if (serialized && page != null) { + if (pageTaken == false && page != null) { page.releaseBlocks(); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java index b7dacdf2a6ea0..0b2cb20ecdabd 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java @@ -157,7 +157,7 @@ void fetchPage() { // finish other sinks if one of them failed or sources no longer need pages. boolean toFinishSinks = buffer.noMoreInputs() || failure.get() != null; remoteSink.fetchPageAsync(toFinishSinks, ActionListener.wrap(resp -> { - Page page = resp.page(); + Page page = resp.takePage(); if (page != null) { buffer.addPage(page); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java index c94320a9d406a..98f069de7517c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeServiceTests.java @@ -345,7 +345,7 @@ public void testEarlyTerminate() { sinkExchanger.fetchPageAsync(true, future); ExchangeResponse resp = future.actionGet(); assertTrue(resp.finished()); - assertNull(resp.page()); + assertNull(resp.takePage()); assertTrue(sink.waitForWriting().isDone()); assertTrue(sink.isFinished()); } @@ -394,8 +394,9 @@ public void messageReceived( @Override public void sendResponse(TransportResponse response) throws IOException { ExchangeResponse exchangeResponse = (ExchangeResponse) response; - if (exchangeResponse.page() != null) { - IntBlock block = exchangeResponse.page().getBlock(0); + Page page = exchangeResponse.takePage(); + if (page != null) { + IntBlock block = page.getBlock(0); for (int i = 0; i < block.getPositionCount(); i++) { if (block.getInt(i) == disconnectOnSeqNo) { throw new IOException("page is too large"); From 3911a28a971a9c7ce74b969fcb7808f52a286c91 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Thu, 28 Sep 2023 08:34:57 +0200 Subject: [PATCH 111/155] Optimize field name matching in field caps API (#99977) When using field caps name patterns, a large share (as in 50%+) of the CPU runtime can go into matching field names because all fields are iterated for ever pattern provided. We can significantly improve this case by pre-compiling the predicate which seems to speedup both the case without matching any field names and the slow case of multiple patterns. --- .../fieldcaps/FieldCapabilitiesFetcher.java | 22 ++++++++----------- .../TransportFieldCapabilitiesAction.java | 5 ++++- .../index/mapper/FieldTypeLookup.java | 2 +- .../index/query/QueryRewriteContext.java | 12 ++++++++++ .../FieldCapabilitiesFilterTests.java | 16 +++++++------- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java index d9b1d8648e4f6..7dc73940ce2ff 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -49,7 +48,7 @@ class FieldCapabilitiesFetcher { FieldCapabilitiesIndexResponse fetch( CancellableTask task, ShardId shardId, - String[] fieldPatterns, + Predicate fieldNameFilter, String[] filters, String[] fieldTypes, QueryBuilder indexFilter, @@ -63,7 +62,7 @@ FieldCapabilitiesIndexResponse fetch( return doFetch( task, shardId, - fieldPatterns, + fieldNameFilter, filters, fieldTypes, indexFilter, @@ -78,7 +77,7 @@ FieldCapabilitiesIndexResponse fetch( private FieldCapabilitiesIndexResponse doFetch( CancellableTask task, ShardId shardId, - String[] fieldPatterns, + Predicate fieldNameFilter, String[] filters, String[] fieldTypes, QueryBuilder indexFilter, @@ -112,7 +111,7 @@ private FieldCapabilitiesIndexResponse doFetch( Predicate fieldPredicate = indicesService.getFieldFilter().apply(shardId.getIndexName()); final Map responseMap = retrieveFieldCaps( searchExecutionContext, - fieldPatterns, + fieldNameFilter, filters, fieldTypes, fieldPredicate @@ -125,23 +124,20 @@ private FieldCapabilitiesIndexResponse doFetch( static Map retrieveFieldCaps( SearchExecutionContext context, - String[] fieldPatterns, + Predicate fieldNameFilter, String[] filters, String[] types, Predicate indexFieldfilter ) { - - Set fieldNames = new HashSet<>(); - for (String pattern : fieldPatterns) { - fieldNames.addAll(context.getMatchingFieldNames(pattern)); - } - boolean includeParentObjects = checkIncludeParents(filters); Predicate filter = buildFilter(indexFieldfilter, filters, types, context); boolean isTimeSeriesIndex = context.getIndexSettings().getTimestampBounds() != null; Map responseMap = new HashMap<>(); - for (String field : fieldNames) { + for (String field : context.getAllFieldNames()) { + if (fieldNameFilter.test(field) == false) { + continue; + } MappedFieldType ft = context.getFieldType(field); if (filter.test(ft)) { IndexFieldCapabilities fieldCap = new IndexFieldCapabilities( diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index 6b579ca8f21d0..91c4f10956866 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; @@ -56,6 +57,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.elasticsearch.action.search.TransportSearchHelper.checkCCSVersionCompatibility; @@ -488,6 +490,7 @@ public void messageReceived(FieldCapabilitiesNodeRequest request, TransportChann .stream() .collect(Collectors.groupingBy(ShardId::getIndexName)); final FieldCapabilitiesFetcher fetcher = new FieldCapabilitiesFetcher(indicesService); + final Predicate fieldNameFilter = Regex.simpleMatcher(request.fields()); for (List shardIds : groupedShardIds.values()) { final Map failures = new HashMap<>(); final Set unmatched = new HashSet<>(); @@ -496,7 +499,7 @@ public void messageReceived(FieldCapabilitiesNodeRequest request, TransportChann final FieldCapabilitiesIndexResponse response = fetcher.fetch( (CancellableTask) task, shardId, - request.fields(), + fieldNameFilter, request.filters(), request.allowedTypes(), request.indexFilter(), diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java index d3cd0e9ca59ed..b9ba0762e5117 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java @@ -162,7 +162,7 @@ private MappedFieldType getDynamicField(String field) { */ Set getMatchingFieldNames(String pattern) { if (Regex.isMatchAllPattern(pattern)) { - return Collections.unmodifiableSet(fullNameToFieldType.keySet()); + return fullNameToFieldType.keySet(); } if (Regex.isSimpleMatchPattern(pattern) == false) { // no wildcards diff --git a/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java index 117203d4a9253..30a85f4941105 100644 --- a/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java +++ b/server/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -9,6 +9,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.util.concurrent.CountDown; @@ -322,4 +323,15 @@ public Set getMatchingFieldNames(String pattern) { } return matches; } + + /** + * Same as {@link #getMatchingFieldNames(String)} with pattern {@code *} but returns an {@link Iterable} instead of a set. + */ + public Iterable getAllFieldNames() { + var allFromMapping = mappingLookup.getMatchingFieldNames("*"); + // runtime mappings and non-runtime fields don't overlap, so we can simply concatenate the iterables here + return runtimeMappings.isEmpty() + ? allFromMapping + : () -> Iterators.concat(allFromMapping.iterator(), runtimeMappings.keySet().iterator()); + } } diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFilterTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFilterTests.java index d0ca8a7bf5d33..182522f2b7d8d 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFilterTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFilterTests.java @@ -38,7 +38,7 @@ public void testExcludeNestedFields() throws IOException { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "-nested" }, Strings.EMPTY_ARRAY, f -> true @@ -64,7 +64,7 @@ public void testMetadataFilters() throws IOException { { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "+metadata" }, Strings.EMPTY_ARRAY, f -> true @@ -75,7 +75,7 @@ public void testMetadataFilters() throws IOException { { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "-metadata" }, Strings.EMPTY_ARRAY, f -> true @@ -106,7 +106,7 @@ public void testExcludeMultifields() throws IOException { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "-multifield" }, Strings.EMPTY_ARRAY, f -> true @@ -135,7 +135,7 @@ public void testDontIncludeParentInfo() throws IOException { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "-parent" }, Strings.EMPTY_ARRAY, f -> true @@ -161,7 +161,7 @@ public void testSecurityFilter() throws IOException { { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY, securityFilter @@ -175,7 +175,7 @@ public void testSecurityFilter() throws IOException { { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, new String[] { "-metadata" }, Strings.EMPTY_ARRAY, securityFilter @@ -201,7 +201,7 @@ public void testFieldTypeFiltering() throws IOException { Map response = FieldCapabilitiesFetcher.retrieveFieldCaps( sec, - new String[] { "*" }, + s -> true, Strings.EMPTY_ARRAY, new String[] { "text", "keyword" }, f -> true From 292722d1fe2c59baa159321b2013f3abeb2e9091 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 28 Sep 2023 09:40:48 +0300 Subject: [PATCH 112/155] ESQL: Ignore exception when closing blocks inside a page (#99991) Fix #99982 --- .../compute/operator/ProjectOperatorTests.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index fa3b2096acb05..baa7842bdc1f9 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -17,13 +17,16 @@ import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.Releasables; import org.elasticsearch.core.Tuple; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.junit.After; import org.junit.Before; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; @@ -71,12 +74,15 @@ public void testProjection() { var out = projection.getOutput(); assertThat(randomProjection.size(), lessThanOrEqualTo(out.getBlockCount())); + Set blks = new HashSet<>(); for (int i = 0; i < out.getBlockCount(); i++) { var block = out.getBlock(i); assertEquals(block, page.getBlock(randomProjection.get(i))); + blks.add(block); } + // close all blocks separately since the same block can be used by multiple columns (aliased) - out.releaseBlocks(); + Releasables.closeWhileHandlingException(blks.toArray(new Block[0])); } private List randomProjection(int size) { From d9c15c526e04bfc96a0f1c55ca8c0a0b9ab58e23 Mon Sep 17 00:00:00 2001 From: Matteo Piergiovanni <134913285+piergm@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:28:45 +0200 Subject: [PATCH 113/155] Add counters to _clusters response for all states (#99566) To help the user know what the possible cluster states are and to provide an accurate accounting, we added counters summarising `running`, `partial` and `failed` clusters to the `_clusters` section. Changes: - Now in the response is present the number of `running` clusters. - We split up `partial` and `successful` (before was summed up in the `successful` counter). - We now have a counter for `failed` clusters. - Now `total` is always equal to `running` + `skipped` + `failed` + `partial` + `successful`. --- docs/changelog/99566.yaml | 6 + .../search-across-clusters.asciidoc | 75 ++++++--- ...rossClusterSearchUnavailableClusterIT.java | 42 +++-- .../org/elasticsearch/search/CCSDuelIT.java | 54 ++++++- .../action/search/CCSPointInTimeIT.java | 9 +- .../search/ccs/CrossClusterSearchIT.java | 66 ++++++-- .../action/search/SearchResponse.java | 135 +++++++++++----- .../action/search/SearchResponseTests.java | 12 +- .../search/TransportSearchActionTests.java | 34 ++-- .../search/CrossClusterAsyncSearchIT.java | 150 ++++++++++++++---- .../search/AsyncSearchResponseTests.java | 11 +- .../search/AsyncStatusResponseTests.java | 41 +++-- .../ml/datafeed/extractor/DataExtractor.java | 4 +- 13 files changed, 470 insertions(+), 169 deletions(-) create mode 100644 docs/changelog/99566.yaml diff --git a/docs/changelog/99566.yaml b/docs/changelog/99566.yaml new file mode 100644 index 0000000000000..caad871bf58ed --- /dev/null +++ b/docs/changelog/99566.yaml @@ -0,0 +1,6 @@ +pr: 99566 +summary: Add additional counters to `_clusters` response for all Cluster search states +area: Search +type: enhancement +issues: + - 98927 diff --git a/docs/reference/search/search-your-data/search-across-clusters.asciidoc b/docs/reference/search/search-your-data/search-across-clusters.asciidoc index 39c272cfc6b78..e1b96afd82196 100644 --- a/docs/reference/search/search-your-data/search-across-clusters.asciidoc +++ b/docs/reference/search/search-your-data/search-across-clusters.asciidoc @@ -146,12 +146,15 @@ included to provide information about the search on each cluster. "skipped": 0 }, "_clusters": { - "total": 1, + "total": 1, <1> "successful": 1, "skipped": 0, + "running": 0, + "partial": 0, + "failed": 0, "details": { - "cluster_one": { <1> - "status": "successful", <2> + "cluster_one": { <2> + "status": "successful", "indices": "my-index-000001", <3> "took": 148, <4> "timed_out": false, @@ -200,12 +203,13 @@ included to provide information about the search on each cluster. // TESTRESPONSE[s/"skipped": 0/"skipped": "$body._shards.skipped"/] // TESTRESPONSE[s/"took": 148/"took": "$body._clusters.details.cluster_one.took"/] -<1> The `_clusters/details` section shows metadata about the search on each cluster. -<2> The cluster status can be one of: *running*, *successful* (searches on all shards -were successful), *partial* (searches on at least one shard of the cluster was successful -and at least one failed), *skipped* (the search failed on a cluster marked with -`skip_unavailable`=`true`) or *failed* (the search failed on a cluster marked with -`skip_unavailable`=`false`). +<1> This section of counters shows all possible cluster search states and how many cluster +searches are currently in that state. The clusters can be one of the following statuses: *running*, +*successful* (searches on all shards were successful), *partial* (searches on at least +one shard of the cluster was successful and at least one failed), *skipped* (the search +failed on a cluster marked with `skip_unavailable`=`true`) or *failed* (the search +failed on a cluster marked with `skip_unavailable`=`false`). +<2> The `_clusters/details` section shows metadata about the search on each cluster. <3> The index expression supplied by the user. If you provide a wildcard such as `logs-*`, this section will show the value with the wildcard, not the concrete indices being searched. <4> How long (in milliseconds) the sub-search took on that cluster. @@ -258,6 +262,9 @@ The API returns the following response: "total": 3, "successful": 3, "skipped": 0, + "running": 0, + "partial": 0, + "failed": 0, "details": { "(local)": { <1> "status": "successful", @@ -434,6 +441,9 @@ The API returns the following response: "total" : 3, "successful" : 0, "skipped": 0, + "running": 3, + "partial": 0, + "failed": 0, "details": { "(local)": { "status": "running", @@ -473,7 +483,7 @@ across all clusters only when the search is completed. When `ccs_minimize_roundtrips`= `false`, the total shard count is known up front and will be correct. <3> The `_clusters` section indicates that 3 clusters are in scope for the search -and all are currently running. +and all are currently in the "running" state. If you query the <> endpoint while the query is still running, you will see an update in the `_clusters` and `_shards` section of @@ -509,6 +519,9 @@ Response: "total": 3, "successful": 1, <2> "skipped": 0, + "running": 2, + "partial": 0, + "failed": 0, "details": { "(local)": { "status": "successful", @@ -550,7 +563,7 @@ Response: <1> All the local cluster shards have completed. <2> The local cluster search has completed, so the "successful" clusters entry -is set to 1. The `_clusters` response metadata will be updated as each cluster +is set to 1 and "running" clusters entry reduced to 2. The `_clusters` response metadata will be updated as each cluster finishes. <3> Number of hits from the local cluster search. Final hits are not shown until searches on all clusters have been completed and merged. @@ -593,6 +606,9 @@ Response: "total": 3, "successful": 3, <3> "skipped": 0, + "running": 0, + "partial": 0, + "failed": 0, "details": { "(local)": { "status": "successful", @@ -742,8 +758,11 @@ Response: }, "_clusters": { "total": 3, - "successful": 3, <3> + "successful": 2, "skipped": 0, + "running": 0, + "partial": 1, <3> + "failed": 0, "details": { "(local)": { "status": "successful", @@ -810,7 +829,7 @@ Response: <1> The search results are marked as partial, since at least one shard search failed. <2> The `_shards` section includes shard failure info. -<3> Clusters that have partial results are still marked as successful. They are +<3> Clusters that have partial results are still marked as "partial". They are marked with status "skipped" (or "failed") only if no data was returned from the search. <4> The `partial` status has been applied to the cluster with partial results. <5> The failed shard count is shown. @@ -858,7 +877,10 @@ Response: "_clusters": { "total": 3, "successful": 1, - "skipped": 2, <2> + "skipped": 1, + "running": 0, + "partial": 0, + "failed": 1, "details": { "(local)": { "status": "successful", @@ -873,7 +895,7 @@ Response: } }, "cluster_one": { - "status": "skipped", <3> + "status": "skipped", <2> "indices": "my-index-000001", "timed_out": false, "failures": [ @@ -881,14 +903,14 @@ Response: "shard": -1, "index": null, "reason": { - "type": "node_disconnected_exception", <4> + "type": "node_disconnected_exception", <3> "reason": "[myhostname1][35.238.149.1:9300][indices:data/read/search] disconnected" } } ] }, "cluster_two": { - "status": "failed", <5> + "status": "failed", <4> "indices": "my-index-000001", "timed_out": false, "failures": [ @@ -907,7 +929,7 @@ Response: "hits": { }, } - "error": { <6> + "error": { <5> "type": "status_exception", "reason": "error while executing search", "caused_by": { @@ -921,15 +943,14 @@ Response: <1> The shard accounting will often be only partial when errors like this occur, since we need to be able to get shard info from remote clusters on each search. -<2> The skipped counter is used for both "skipped" and "failed" clusters. -<3> `cluster_one` disconnected during the search and it returned no results. +<2> `cluster_one` disconnected during the search and it returned no results. Since it is marked in the remote cluster configuration as `skip_unavailable`=`true`, its status is "skipped", which will not fail the entire search. -<4> The failures list shows that the remote cluster node disconnected from the +<3> The failures list shows that the remote cluster node disconnected from the querying cluster. -<5> `cluster_two` status is "failed", since it is marked in the remote cluster +<4> `cluster_two` status is "failed", since it is marked in the remote cluster configuration as `skip_unavailable`=`false`. -<6> A top level `error` entry is included when there is a "failed" cluster. +<5> A top level `error` entry is included when there is a "failed" cluster. [discrete] @@ -1060,7 +1081,10 @@ the `wait_for_completion_timeout` duration (see <>). "total" : 3, "successful": 0, "skipped": 0, - "details": { <2> + "running": 3, <2> + "partial": 0, + "failed": 0, + "details": { <3> "(local)": { "status": "running", "indices": "my-index-000001", @@ -1112,7 +1136,8 @@ the `wait_for_completion_timeout` duration (see <>). <1> All shards from all clusters in scope for the search are listed here. Watch this section and/or the _clusters section for updates to monitor search progress. -<2> The `_clusters` section shows that shard information was successfully +<2> From the `_clusters` section we can see that all the clusters are in "running" state. +<3> The `_clusters` section shows that shard information was successfully gathered from all 3 clusters and the total shard count on each cluster is listed. diff --git a/qa/ccs-unavailable-clusters/src/javaRestTest/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/javaRestTest/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java index 5429562ac084a..3279777c793ba 100644 --- a/qa/ccs-unavailable-clusters/src/javaRestTest/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/javaRestTest/java/org/elasticsearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -190,16 +190,22 @@ public void testSearchSkipUnavailable() throws IOException { { SearchResponse response = restHighLevelClient.search(new SearchRequest("index", "remote1:index"), RequestOptions.DEFAULT); assertEquals(2, response.getClusters().getTotal()); - assertEquals(2, response.getClusters().getSuccessful()); - assertEquals(0, response.getClusters().getSkipped()); + assertEquals(2, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } { SearchResponse response = restHighLevelClient.search(new SearchRequest("remote1:index"), RequestOptions.DEFAULT); assertEquals(1, response.getClusters().getTotal()); - assertEquals(1, response.getClusters().getSuccessful()); - assertEquals(0, response.getClusters().getSkipped()); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(0, response.getHits().getTotalHits().value); } @@ -209,8 +215,11 @@ public void testSearchSkipUnavailable() throws IOException { RequestOptions.DEFAULT ); assertEquals(2, response.getClusters().getTotal()); - assertEquals(2, response.getClusters().getSuccessful()); - assertEquals(0, response.getClusters().getSkipped()); + assertEquals(2, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); @@ -227,16 +236,22 @@ public void testSearchSkipUnavailable() throws IOException { { SearchResponse response = restHighLevelClient.search(new SearchRequest("index", "remote1:index"), RequestOptions.DEFAULT); assertEquals(2, response.getClusters().getTotal()); - assertEquals(1, response.getClusters().getSuccessful()); - assertEquals(1, response.getClusters().getSkipped()); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); } { SearchResponse response = restHighLevelClient.search(new SearchRequest("remote1:index"), RequestOptions.DEFAULT); assertEquals(1, response.getClusters().getTotal()); - assertEquals(0, response.getClusters().getSuccessful()); - assertEquals(1, response.getClusters().getSkipped()); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(0, response.getHits().getTotalHits().value); } @@ -246,8 +261,11 @@ public void testSearchSkipUnavailable() throws IOException { RequestOptions.DEFAULT ); assertEquals(2, response.getClusters().getTotal()); - assertEquals(1, response.getClusters().getSuccessful()); - assertEquals(1, response.getClusters().getSkipped()); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(1, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, response.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(10, response.getHits().getTotalHits().value); assertEquals(10, response.getHits().getHits().length); String scrollId = response.getScrollId(); diff --git a/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java b/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java index 748cb2d95918e..8ca4ce7a4eb2f 100644 --- a/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java +++ b/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java @@ -1072,8 +1072,26 @@ private static Map duelSearchSync(SearchRequest searchRequest, C SearchResponse.Clusters clustersMRTFalse = fanOutSearchResponse.getClusters(); assertEquals(clustersMRT.getTotal(), clustersMRTFalse.getTotal()); - assertEquals(clustersMRT.getSuccessful(), clustersMRTFalse.getSuccessful()); - assertEquals(clustersMRT.getSkipped(), clustersMRTFalse.getSkipped()); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.FAILED) + ); Map minimizeRoundtripsResponseMap = responseToMap(minimizeRoundtripsSearchResponse); if (clustersMRT.hasClusterObjects() && clustersMRTFalse.hasClusterObjects()) { @@ -1144,8 +1162,26 @@ private static Map duelSearchAsync(SearchRequest searchRequest, SearchResponse.Clusters clustersMRTFalse = fanOutSearchResponse.getClusters(); assertEquals(clustersMRT.getTotal(), clustersMRTFalse.getTotal()); - assertEquals(clustersMRT.getSuccessful(), clustersMRTFalse.getSuccessful()); - assertEquals(clustersMRT.getSkipped(), clustersMRTFalse.getSkipped()); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL) + ); + assertEquals( + clustersMRT.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), + clustersMRTFalse.getClusterStateCount(SearchResponse.Cluster.Status.FAILED) + ); Map minimizeRoundtripsResponseMap = responseToMap(minimizeRoundtripsSearchResponse); if (clustersMRT.hasClusterObjects() && clustersMRTFalse.hasClusterObjects()) { @@ -1229,14 +1265,20 @@ static ContentType createContentType(final XContentType xContentType) { private static void assertMultiClusterSearchResponse(SearchResponse searchResponse) { assertEquals(2, searchResponse.getClusters().getTotal()); - assertEquals(2, searchResponse.getClusters().getSuccessful()); + // for bwc checks we expect SUCCESSFUL + PARTIAL to be equal to 2 + int bwcSuccessful = searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL); + bwcSuccessful += searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL); + assertEquals(2, bwcSuccessful); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertThat(searchResponse.getTotalShards(), greaterThan(1)); assertThat(searchResponse.getSuccessfulShards(), greaterThan(1)); } private static void assertSingleRemoteClusterSearchResponse(SearchResponse searchResponse) { assertEquals(1, searchResponse.getClusters().getTotal()); - assertEquals(1, searchResponse.getClusters().getSuccessful()); + assertEquals(1, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); assertThat(searchResponse.getTotalShards(), greaterThanOrEqualTo(1)); assertThat(searchResponse.getSuccessfulShards(), greaterThanOrEqualTo(1)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/CCSPointInTimeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/CCSPointInTimeIT.java index 390a3a8bdac4c..fdce24ea21366 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/CCSPointInTimeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/CCSPointInTimeIT.java @@ -108,8 +108,8 @@ public void testBasic() { SearchResponse.Clusters clusters = resp.getClusters(); int expectedNumClusters = 1 + (includeLocalIndex ? 1 : 0); assertThat(clusters.getTotal(), equalTo(expectedNumClusters)); - assertThat(clusters.getSuccessful(), equalTo(expectedNumClusters)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(expectedNumClusters)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); if (includeLocalIndex) { AtomicReference localClusterRef = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); @@ -163,8 +163,9 @@ public void testFailuresOnOneShardsWithPointInTime() throws ExecutionException, SearchResponse.Clusters clusters = searchResponse.getClusters(); int expectedNumClusters = 1 + (includeLocalIndex ? 1 : 0); assertThat(clusters.getTotal(), equalTo(expectedNumClusters)); - assertThat(clusters.getSuccessful(), equalTo(expectedNumClusters)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(expectedNumClusters)); if (includeLocalIndex) { AtomicReference localClusterRef = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/ccs/CrossClusterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/ccs/CrossClusterSearchIT.java index ccc7d6bc5009c..c68de1b28e2c3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/ccs/CrossClusterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/ccs/CrossClusterSearchIT.java @@ -125,8 +125,11 @@ public void testClusterDetailsAfterSuccessfulCCS() throws Exception { SearchResponse.Clusters clusters = searchResponse.getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -186,8 +189,11 @@ public void testCCSClusterDetailsWhereAllShardsSkippedInCanMatch() throws Except SearchResponse.Clusters clusters = searchResponse.getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -250,8 +256,11 @@ public void testClusterDetailsAfterCCSWithFailuresOnOneShardOnly() throws Except SearchResponse.Clusters clusters = searchResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -314,8 +323,16 @@ public void testClusterDetailsAfterCCSWithFailuresOnRemoteClusterOnly() throws E assertThat(clusters.isCcsMinimizeRoundtrips(), equalTo(minimizeRoundtrips)); } assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -386,8 +403,11 @@ public void testCCSWithSearchTimeoutOnRemoteCluster() throws Exception { SearchResponse.Clusters clusters = searchResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -439,8 +459,11 @@ public void testRemoteClusterOnlyCCSSuccessfulResult() throws Exception { SearchResponse.Clusters clusters = searchResponse.getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -482,8 +505,11 @@ public void testRemoteClusterOnlyCCSWithFailuresOnOneShardOnly() throws Exceptio SearchResponse.Clusters clusters = searchResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -530,8 +556,16 @@ public void testRemoteClusterOnlyCCSWithFailuresOnAllShards() throws Exception { assertNotNull(searchResponse); SearchResponse.Clusters clusters = searchResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(0)); - assertThat(clusters.getSkipped(), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 278d1bbdf5207..f9f5f43494711 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -460,14 +460,20 @@ public static class Clusters implements ToXContentFragment, Writeable { public static final Clusters EMPTY = new Clusters(0, 0, 0); static final ParseField _CLUSTERS_FIELD = new ParseField("_clusters"); + static final ParseField TOTAL_FIELD = new ParseField("total"); static final ParseField SUCCESSFUL_FIELD = new ParseField("successful"); static final ParseField SKIPPED_FIELD = new ParseField("skipped"); - static final ParseField TOTAL_FIELD = new ParseField("total"); + static final ParseField RUNNING_FIELD = new ParseField("running"); + static final ParseField PARTIAL_FIELD = new ParseField("partial"); + static final ParseField FAILED_FIELD = new ParseField("failed"); static final ParseField DETAILS_FIELD = new ParseField("details"); private final int total; - private final int successful; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map - private final int skipped; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map + private final int successful; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map + private final int skipped; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map + private final int running; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map + private final int partial; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map + private final int failed; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map // key to map is clusterAlias on the primary querying cluster of a CCS minimize_roundtrips=true query // the Map itself is immutable after construction - all Clusters will be accounted for at the start of the search @@ -479,9 +485,9 @@ public static class Clusters implements ToXContentFragment, Writeable { /** * For use with cross-cluster searches. - * When minimizing roundtrips, the number of successful and skipped clusters is not known until - * the end of the search and it the information in SearchResponse.Cluster object will be updated - * as each cluster returns. + * When minimizing roundtrips, the number of successful, skipped, running, partial and failed clusters + * is not known until the end of the search and it the information in SearchResponse.Cluster object + * will be updated as each cluster returns. * @param localIndices The localIndices to be searched - null if no local indices are to be searched * @param remoteClusterIndices mapping of clusterAlias -> OriginalIndices for each remote cluster * @param ccsMinimizeRoundtrips whether minimizing roundtrips for the CCS @@ -496,8 +502,7 @@ public Clusters( ) { assert remoteClusterIndices.size() > 0 : "At least one remote cluster must be passed into this Cluster constructor"; this.total = remoteClusterIndices.size() + (localIndices == null ? 0 : 1); - this.successful = 0; // calculated from clusterInfo map for minimize_roundtrips - this.skipped = 0; // calculated from clusterInfo map for minimize_roundtrips + assert total >= 1 : "No local indices or remote clusters passed in"; this.ccsMinimizeRoundtrips = ccsMinimizeRoundtrips; Map> m = new HashMap<>(); if (localIndices != null) { @@ -512,6 +517,11 @@ public Clusters( m.put(clusterAlias, new AtomicReference<>(c)); } this.clusterInfo = Collections.unmodifiableMap(m); + this.successful = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.SUCCESSFUL); + this.skipped = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.SKIPPED); + this.running = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.RUNNING); + this.partial = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.PARTIAL); + this.failed = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.FAILED); } /** @@ -529,6 +539,9 @@ public Clusters(int total, int successful, int skipped) { this.total = total; this.successful = successful; this.skipped = skipped; + this.running = 0; + this.partial = 0; + this.failed = 0; this.ccsMinimizeRoundtrips = false; this.clusterInfo = Collections.emptyMap(); // will never be used if created from this constructor } @@ -549,17 +562,35 @@ public Clusters(StreamInput in) throws IOException { } else { this.clusterInfo = Collections.emptyMap(); } + this.running = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.RUNNING); + this.partial = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.PARTIAL); + this.failed = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.FAILED); + this.ccsMinimizeRoundtrips = false; assert total >= 0 : "total is negative: " + total; - assert total >= successful + skipped - : "successful + skipped is larger than total. total: " + total + " successful: " + successful + " skipped: " + skipped; + assert total >= successful + skipped + running + partial + failed + : "successful + skipped + running + partial + failed is larger than total. total: " + + total + + " successful: " + + successful + + " skipped: " + + skipped + + " running: " + + running + + " partial: " + + partial + + " failed: " + + failed; } private Clusters(Map> clusterInfoMap) { assert clusterInfoMap.size() > 0 : "this constructor should not be called with an empty Cluster info map"; this.total = clusterInfoMap.size(); this.clusterInfo = clusterInfoMap; - this.successful = 0; // calculated from clusterInfo map for minimize_roundtrips - this.skipped = 0; // calculated from clusterInfo map for minimize_roundtrips + this.successful = 0; // calculated from clusterInfo map for minimize_roundtrips + this.skipped = 0; // calculated from clusterInfo map for minimize_roundtrips + this.running = 0; // calculated from clusterInfo map for minimize_roundtrips + this.partial = 0; // calculated from clusterInfo map for minimize_roundtrips + this.failed = 0; // calculated from clusterInfo map for minimize_roundtrips // should only be called if "details" section of fromXContent is present (for ccsMinimizeRoundtrips) this.ccsMinimizeRoundtrips = true; } @@ -584,9 +615,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (total > 0) { builder.startObject(_CLUSTERS_FIELD.getPreferredName()); builder.field(TOTAL_FIELD.getPreferredName(), total); - builder.field(SUCCESSFUL_FIELD.getPreferredName(), getSuccessful()); - builder.field(SKIPPED_FIELD.getPreferredName(), getSkipped()); - // TODO: add FAILED_FIELD + builder.field(SUCCESSFUL_FIELD.getPreferredName(), getClusterStateCount(Cluster.Status.SUCCESSFUL)); + builder.field(SKIPPED_FIELD.getPreferredName(), getClusterStateCount(Cluster.Status.SKIPPED)); + builder.field(RUNNING_FIELD.getPreferredName(), getClusterStateCount(Cluster.Status.RUNNING)); + builder.field(PARTIAL_FIELD.getPreferredName(), getClusterStateCount(Cluster.Status.PARTIAL)); + builder.field(FAILED_FIELD.getPreferredName(), getClusterStateCount(Cluster.Status.FAILED)); if (clusterInfo.size() > 0) { builder.startObject("details"); for (AtomicReference cluster : clusterInfo.values()) { @@ -602,21 +635,30 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public static Clusters fromXContent(XContentParser parser) throws IOException { XContentParser.Token token = parser.currentToken(); ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser); - int successful = -1; int total = -1; + int successful = -1; int skipped = -1; + int running = 0; // 0 for BWC + int partial = 0; // 0 for BWC + int failed = 0; // 0 for BWC Map> clusterInfoMap = new HashMap<>(); String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token.isValue()) { - if (Clusters.SUCCESSFUL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - successful = parser.intValue(); - } else if (Clusters.TOTAL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + if (Clusters.TOTAL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { total = parser.intValue(); + } else if (Clusters.SUCCESSFUL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + successful = parser.intValue(); } else if (Clusters.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skipped = parser.intValue(); + } else if (Clusters.RUNNING_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + running = parser.intValue(); + } else if (Clusters.PARTIAL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + partial = parser.intValue(); + } else if (Clusters.FAILED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + failed = parser.intValue(); } else { parser.skipChildren(); } @@ -641,6 +683,8 @@ public static Clusters fromXContent(XContentParser parser) throws IOException { } } if (clusterInfoMap.isEmpty()) { + assert running == 0 && partial == 0 && failed == 0 + : "Non cross-cluster should have counter for running, partial and failed equal to 0"; return new Clusters(total, successful, skipped); } else { return new Clusters(clusterInfoMap); @@ -655,15 +699,20 @@ public int getTotal() { } /** - * @return how many total clusters the search was executed successfully on + * @param status the state you want to query + * @return how many clusters are currently in a specific state */ - public int getSuccessful() { + public int getClusterStateCount(Cluster.Status status) { if (clusterInfo.isEmpty()) { - return successful; + return switch (status) { + case RUNNING -> running; + case SUCCESSFUL -> successful; + case PARTIAL -> partial; + case SKIPPED -> skipped; + case FAILED -> failed; + }; } else { - return determineCountFromClusterInfo( - cluster -> cluster.getStatus() == Cluster.Status.SUCCESSFUL || cluster.getStatus() == Cluster.Status.PARTIAL - ); + return determineCountFromClusterInfo(cluster -> cluster.getStatus() == status); } } @@ -678,19 +727,6 @@ private int determineCountFromClusterInfo(Predicate predicate) { return (int) clusterInfo.values().stream().filter(c -> predicate.test(c.get())).count(); } - /** - * @return how many total clusters were used during the execution of the search request - */ - public int getSkipped() { - if (clusterInfo.isEmpty()) { - return skipped; - } else { - return determineCountFromClusterInfo(cluster -> - // TODO: change this after adding an XContent field for FAILED clusters - cluster.getStatus() == Cluster.Status.SKIPPED || cluster.getStatus() == Cluster.Status.FAILED); - } - } - /** * @return whether this search was a cross cluster search done with ccsMinimizeRoundtrips=true */ @@ -715,17 +751,34 @@ public boolean equals(Object o) { return false; } Clusters clusters = (Clusters) o; - return total == clusters.total && successful == clusters.successful && skipped == clusters.skipped; + return total == clusters.total + && successful == clusters.successful + && skipped == clusters.skipped + && running == clusters.running + && partial == clusters.partial + && failed == clusters.failed; } @Override public int hashCode() { - return Objects.hash(total, successful, skipped); + return Objects.hash(total, successful, skipped, running, partial, failed); } @Override public String toString() { - return "Clusters{total=" + total + ", successful=" + getSuccessful() + ", skipped=" + getSkipped() + '}'; + return "Clusters{total=" + + total + + ", successful=" + + getClusterStateCount(Cluster.Status.SUCCESSFUL) + + ", skipped=" + + getClusterStateCount(Cluster.Status.SKIPPED) + + ", running=" + + getClusterStateCount(Cluster.Status.RUNNING) + + ", partial=" + + getClusterStateCount(Cluster.Status.PARTIAL) + + ", failed=" + + getClusterStateCount(Cluster.Status.FAILED) + + '}'; } /** diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index eed21c2e2e17f..befea803e6fa0 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -430,7 +430,10 @@ public void testToXContent() throws IOException { "_clusters": { "total": 5, "successful": 3, - "skipped": 2 + "skipped": 2, + "running":0, + "partial": 0, + "failed": 0 }, "hits": { "total": { @@ -483,8 +486,11 @@ public void testToXContent() throws IOException { }, "_clusters": { "total": 4, - "successful": 2, - "skipped": 2, + "successful": 1, + "skipped": 1, + "running":0, + "partial": 1, + "failed": 1, "details": { "(local)": { "status": "successful", diff --git a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java index dae91fe883a30..366161881d30f 100644 --- a/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/TransportSearchActionTests.java @@ -613,9 +613,12 @@ public void testCCSRemoteReduce() throws Exception { awaitLatch(latch, 5, TimeUnit.SECONDS); SearchResponse searchResponse = response.get(); - assertEquals(0, searchResponse.getClusters().getSkipped()); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); - assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); + assertEquals(totalClusters, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); assertEquals(totalClusters == 1 ? 1 : totalClusters + 1, searchResponse.getNumReducePhases()); } { @@ -758,10 +761,16 @@ public void onNodeDisconnected(DiscoveryNode node, Transport.Connection connecti awaitLatch(latch, 5, TimeUnit.SECONDS); SearchResponse searchResponse = response.get(); - assertEquals(disconnectedNodesIndices.size(), searchResponse.getClusters().getSkipped()); + assertEquals( + disconnectedNodesIndices.size(), + searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED) + ); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); int successful = totalClusters - disconnectedNodesIndices.size(); - assertEquals(successful, searchResponse.getClusters().getSuccessful()); + assertEquals(successful, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(successful == 0 ? 0 : successful + 1, searchResponse.getNumReducePhases()); } @@ -816,9 +825,12 @@ public void onNodeDisconnected(DiscoveryNode node, Transport.Connection connecti awaitLatch(latch, 5, TimeUnit.SECONDS); SearchResponse searchResponse = response.get(); - assertEquals(0, searchResponse.getClusters().getSkipped()); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); assertEquals(totalClusters, searchResponse.getClusters().getTotal()); - assertEquals(totalClusters, searchResponse.getClusters().getSuccessful()); + assertEquals(totalClusters, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.RUNNING)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(0, searchResponse.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertEquals(totalClusters == 1 ? 1 : totalClusters + 1, searchResponse.getNumReducePhases()); }); assertEquals(0, service.getConnectionManager().size()); @@ -877,7 +889,7 @@ public void testCollectSearchShards() throws Exception { SearchShardsResponse shardsResponse = map.get(clusterAlias); assertThat(shardsResponse.getNodes(), hasSize(1)); } - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); } { final CountDownLatch latch = new CountDownLatch(1); @@ -897,7 +909,7 @@ public void testCollectSearchShards() throws Exception { new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch) ); awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(numClusters, clusters.getSkipped()); + assertEquals(numClusters, clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertNotNull(failure.get()); assertThat(failure.get(), instanceOf(RemoteTransportException.class)); RemoteTransportException remoteTransportException = (RemoteTransportException) failure.get(); @@ -945,7 +957,7 @@ public void onNodeDisconnected(DiscoveryNode node, Transport.Connection connecti new LatchedActionListener<>(ActionListener.wrap(r -> fail("no response expected"), failure::set), latch) ); awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(numDisconnectedClusters, clusters.getSkipped()); + assertEquals(numDisconnectedClusters, clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED)); assertNotNull(failure.get()); assertThat(failure.get(), instanceOf(RemoteTransportException.class)); assertThat(failure.get().getMessage(), containsString("error while communicating with remote cluster [")); @@ -978,7 +990,7 @@ public void onNodeDisconnected(DiscoveryNode node, Transport.Connection connecti assertNotNull(response.get()); Map map = response.get(); assertEquals(numClusters - disconnectedNodesIndices.size(), map.size()); - assertEquals(disconnectedNodesIndices.size(), clusters.getSkipped()); + assertEquals(disconnectedNodesIndices.size(), clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); for (int i = 0; i < numClusters; i++) { String clusterAlias = "remote" + i; if (disconnectedNodesIndices.contains(i)) { @@ -1021,7 +1033,7 @@ public void onNodeDisconnected(DiscoveryNode node, Transport.Connection connecti new LatchedActionListener<>(ActionTestUtils.assertNoFailureListener(response::set), latch) ); awaitLatch(latch, 5, TimeUnit.SECONDS); - assertEquals(0, clusters.getSkipped()); + assertEquals(0, clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); assertNotNull(response.get()); Map map = response.get(); assertEquals(numClusters, map.size()); diff --git a/x-pack/plugin/async-search/src/internalClusterTest/java/org/elasticsearch/xpack/search/CrossClusterAsyncSearchIT.java b/x-pack/plugin/async-search/src/internalClusterTest/java/org/elasticsearch/xpack/search/CrossClusterAsyncSearchIT.java index d898763b78714..3f27cb6b3a458 100644 --- a/x-pack/plugin/async-search/src/internalClusterTest/java/org/elasticsearch/xpack/search/CrossClusterAsyncSearchIT.java +++ b/x-pack/plugin/async-search/src/internalClusterTest/java/org/elasticsearch/xpack/search/CrossClusterAsyncSearchIT.java @@ -186,8 +186,11 @@ public void testClusterDetailsAfterSuccessfulCCS() throws Exception { SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -217,8 +220,11 @@ public void testClusterDetailsAfterSuccessfulCCS() throws Exception { SearchResponse.Clusters clusters = statusResponse.getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -298,8 +304,11 @@ public void testCCSClusterDetailsWhereAllShardsSkippedInCanMatch() throws Except SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -368,8 +377,16 @@ public void testClusterDetailsAfterCCSWithFailuresOnAllShards() throws Exception SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(0)); - assertThat(clusters.getSkipped(), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(2)); + } SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -389,8 +406,16 @@ public void testClusterDetailsAfterCCSWithFailuresOnAllShards() throws Exception AsyncStatusResponse statusResponse = getAsyncStatus(response.getId()); SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(0)); - assertThat(clusters.getSkipped(), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(2)); + } SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -458,8 +483,11 @@ public void testClusterDetailsAfterCCSWithFailuresOnOneShardOnly() throws Except AsyncSearchResponse finishedResponse = getAsyncSearch(response.getId()); SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -490,8 +518,11 @@ public void testClusterDetailsAfterCCSWithFailuresOnOneShardOnly() throws Except AsyncStatusResponse statusResponse = getAsyncStatus(response.getId()); SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -578,8 +609,16 @@ public void testClusterDetailsAfterCCSWithFailuresOnOneClusterOnly() throws Exce SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -620,8 +659,16 @@ public void testClusterDetailsAfterCCSWithFailuresOnOneClusterOnly() throws Exce AsyncStatusResponse statusResponse = getAsyncStatus(response.getId()); SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -692,8 +739,11 @@ public void testCCSWithSearchTimeout() throws Exception { SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -725,8 +775,11 @@ public void testCCSWithSearchTimeout() throws Exception { SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(2)); - assertThat(clusters.getSuccessful(), equalTo(2)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(2)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); SearchResponse.Cluster localClusterSearchInfo = clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY).get(); assertNotNull(localClusterSearchInfo); @@ -788,8 +841,11 @@ public void testRemoteClusterOnlyCCSSuccessfulResult() throws Exception { SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -811,8 +867,11 @@ public void testRemoteClusterOnlyCCSSuccessfulResult() throws Exception { SearchResponse.Clusters clusters = statusResponse.getClusters(); assertFalse("search cluster results should NOT be marked as partial", clusters.hasPartialResults()); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -862,8 +921,11 @@ public void testRemoteClusterOnlyCCSWithFailuresOnOneShardOnly() throws Exceptio SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -884,8 +946,11 @@ public void testRemoteClusterOnlyCCSWithFailuresOnOneShardOnly() throws Exceptio AsyncStatusResponse statusResponse = getAsyncStatus(response.getId()); SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(1)); - assertThat(clusters.getSkipped(), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -940,8 +1005,16 @@ public void testRemoteClusterOnlyCCSWithFailuresOnAllShards() throws Exception { SearchResponse.Clusters clusters = finishedResponse.getSearchResponse().getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(0)); - assertThat(clusters.getSkipped(), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); @@ -958,9 +1031,16 @@ public void testRemoteClusterOnlyCCSWithFailuresOnAllShards() throws Exception { AsyncStatusResponse statusResponse = getAsyncStatus(response.getId()); SearchResponse.Clusters clusters = statusResponse.getClusters(); assertThat(clusters.getTotal(), equalTo(1)); - assertThat(clusters.getSuccessful(), equalTo(0)); - assertThat(clusters.getSkipped(), equalTo(1)); - + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), equalTo(0)); + if (skipUnavailable) { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(1)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(0)); + } else { + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), equalTo(0)); + assertThat(clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), equalTo(1)); + } assertNull(clusters.getCluster(RemoteClusterAware.LOCAL_CLUSTER_GROUP_KEY)); SearchResponse.Cluster remoteClusterSearchInfo = clusters.getCluster(REMOTE_CLUSTER).get(); diff --git a/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncSearchResponseTests.java b/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncSearchResponseTests.java index aff63cd32976a..1fee87de2f134 100644 --- a/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncSearchResponseTests.java +++ b/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncSearchResponseTests.java @@ -357,6 +357,9 @@ public void testToXContentWithCCSSearchResponseWhileRunning() throws IOException "total" : 3, "successful" : 0, "skipped" : 0, + "running" : 3, + "partial" : 0, + "failed" : 0, "details" : { "cluster_1" : { "status" : "running", @@ -413,6 +416,9 @@ public void testToXContentWithCCSSearchResponseWhileRunning() throws IOException "total" : 3, "successful" : 0, "skipped" : 0, + "running" : 3, + "partial" : 0, + "failed" : 0, "details" : { "cluster_1" : { "status" : "running", @@ -575,8 +581,11 @@ public void testToXContentWithCCSSearchResponseAfterCompletion() throws IOExcept }, "_clusters" : { "total" : 4, - "successful" : 3, + "successful" : 2, "skipped" : 1, + "running" : 0, + "partial" : 1, + "failed" : 0, "details" : { "(local)" : { "status" : "successful", diff --git a/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncStatusResponseTests.java b/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncStatusResponseTests.java index aa4ad8aa6288e..95a72014740fa 100644 --- a/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncStatusResponseTests.java +++ b/x-pack/plugin/async-search/src/test/java/org/elasticsearch/xpack/search/AsyncStatusResponseTests.java @@ -153,8 +153,11 @@ public void testToXContent() throws IOException { response.getSkippedShards(), response.getFailedShards(), clusters.getTotal(), - clusters.getSuccessful(), - clusters.getSkipped(), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), response.getCompletionStatus() == null ? "" : Strings.format(""" ,"completion_status" : %s""", response.getCompletionStatus().getStatus()) }; @@ -175,7 +178,10 @@ public void testToXContent() throws IOException { "_clusters": { "total": %s, "successful": %s, - "skipped": %s + "skipped": %s, + "running": %s, + "partial": %s, + "failed": %s } %s } @@ -193,8 +199,11 @@ public void testToXContent() throws IOException { response.getSkippedShards(), response.getFailedShards(), clusters.getTotal(), - clusters.getSuccessful(), - clusters.getSkipped(), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.RUNNING), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL), + clusters.getClusterStateCount(SearchResponse.Cluster.Status.FAILED), response.getCompletionStatus() == null ? "" : Strings.format(""" ,"completion_status" : %s""", response.getCompletionStatus().getStatus()) }; @@ -216,6 +225,9 @@ public void testToXContent() throws IOException { "total": %s, "successful": %s, "skipped": %s, + "running": %s, + "partial": %s, + "failed": %s, "details": { "(local)": { "status": "running", @@ -373,11 +385,10 @@ public void testGetStatusFromStoredSearchWithNonEmptyClustersSuccessfullyComplet } else { // CCS search totalClusters = 80; - int successful = randomInt(60); + successfulClusters = randomInt(60); int partial = randomInt(20); - successfulClusters = successful + partial; - skippedClusters = totalClusters - successfulClusters; - clusters = AsyncSearchResponseTests.createCCSClusterObjects(80, 80, true, successful, skippedClusters, partial); + skippedClusters = totalClusters - (successfulClusters + partial); + clusters = AsyncSearchResponseTests.createCCSClusterObjects(80, 80, true, successfulClusters, skippedClusters, partial); } SearchResponse searchResponse = new SearchResponse( internalSearchResponse, @@ -396,8 +407,11 @@ public void testGetStatusFromStoredSearchWithNonEmptyClustersSuccessfullyComplet assertEquals(0, statusFromStoredSearch.getFailedShards()); assertEquals(statusFromStoredSearch.getCompletionStatus(), RestStatus.OK); assertEquals(totalClusters, statusFromStoredSearch.getClusters().getTotal()); - assertEquals(skippedClusters, statusFromStoredSearch.getClusters().getSkipped()); - assertEquals(successfulClusters, statusFromStoredSearch.getClusters().getSuccessful()); + assertEquals(skippedClusters, statusFromStoredSearch.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); + assertEquals( + successfulClusters, + statusFromStoredSearch.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL) + ); } @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98706") @@ -432,7 +446,8 @@ public void testGetStatusFromStoredSearchWithNonEmptyClustersStillRunning() { assertEquals(0, statusFromStoredSearch.getFailedShards()); assertNull("completion_status should not be present if still running", statusFromStoredSearch.getCompletionStatus()); assertEquals(100, statusFromStoredSearch.getClusters().getTotal()); - assertEquals(successful + partial, statusFromStoredSearch.getClusters().getSuccessful()); - assertEquals(skipped, statusFromStoredSearch.getClusters().getSkipped()); + assertEquals(successful, statusFromStoredSearch.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SUCCESSFUL)); + assertEquals(partial, statusFromStoredSearch.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.PARTIAL)); + assertEquals(skipped, statusFromStoredSearch.getClusters().getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED)); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/extractor/DataExtractor.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/extractor/DataExtractor.java index 63cd89b30bf87..e432c8d8a9e14 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/extractor/DataExtractor.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/extractor/DataExtractor.java @@ -54,10 +54,10 @@ record Result(SearchInterval searchInterval, Optional data) {} */ default void checkForSkippedClusters(SearchResponse searchResponse) { SearchResponse.Clusters clusterResponse = searchResponse.getClusters(); - if (clusterResponse != null && clusterResponse.getSkipped() > 0) { + if (clusterResponse != null && clusterResponse.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED) > 0) { throw new ResourceNotFoundException( "[{}] remote clusters out of [{}] were skipped when performing datafeed search", - clusterResponse.getSkipped(), + clusterResponse.getClusterStateCount(SearchResponse.Cluster.Status.SKIPPED), clusterResponse.getTotal() ); } From e7fa8e8d1da834f77964ba3e6752dd4d3e2097be Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 28 Sep 2023 10:31:18 +0300 Subject: [PATCH 114/155] Support exists queries for sparse_vector fields (#99775) This commit adds non-null `sparse_vector` fields to `_field_names`, so that we can support `exists` queries & also introduces a new IndexVersion to ensure backwards compatibility. Closes #99319 --- docs/changelog/99775.yaml | 6 +++ .../test/search.vectors/90_sparse_vector.yml | 54 +++++++++++++++++++ .../elasticsearch/search/query/ExistsIT.java | 8 +++ .../org/elasticsearch/index/IndexVersion.java | 1 + .../vectors/SparseVectorFieldMapper.java | 18 +++++-- .../vectors/SparseVectorFieldMapperTests.java | 7 --- 6 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 docs/changelog/99775.yaml diff --git a/docs/changelog/99775.yaml b/docs/changelog/99775.yaml new file mode 100644 index 0000000000000..0c0dbdb1fce87 --- /dev/null +++ b/docs/changelog/99775.yaml @@ -0,0 +1,6 @@ +pr: 99775 +summary: Adding support for exist queries to `sparse_vector` fields +area: Search +type: enhancement +issues: + - 99319 diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml index 2ddb95d6fc139..3644e93d12bbd 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml @@ -15,6 +15,8 @@ type: text ml.tokens: type: sparse_vector + embeddings: + type: sparse_vector - match: { acknowledged: true } @@ -70,6 +72,28 @@ - match: { result: "created" } + - do: + index: + index: test + id: "3" + body: + text: "doing nothing will result in nothing" + ml: + tokens: {} + + - match: { result: "created" } + + - do: + index: + index: test + id: "4" + body: + text: "other embeddings available only" + embeddings: + aardvark: 0.5 + + - match: { result: "created" } + - do: indices.refresh: { } @@ -97,6 +121,26 @@ - match: { hits.hits.0._id: "2" } - match: { hits.hits.1._id: "1" } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + exists: + field: ml.tokens + - match: { hits.total: 3 } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + exists: + field: embeddings + - match: { hits.total: 1 } + --- "Sparse vector in 7.x": - skip: @@ -119,6 +163,16 @@ - match: { acknowledged: true } + - do: + catch: /\[sparse_vector\] fields do not support \[exists\] queries/ + search: + rest_total_hits_as_int: true + index: test + body: + query: + exists: + field: ml.tokens + --- "Sparse vector in 8.x": - skip: diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java index f423ebd8b723a..56ecd865cd707 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java @@ -70,6 +70,9 @@ public void testExists() throws Exception { .endObject() .endObject() .endObject() + .startObject("vec") + .field("type", "sparse_vector") + .endObject() .endObject() .endObject() .endObject(); @@ -85,6 +88,10 @@ public void testExists() throws Exception { // object fields singletonMap("bar", barObject), singletonMap("bar", singletonMap("baz", 42)), + // sparse_vector field empty + singletonMap("vec", emptyMap()), + // sparse_vector field non-empty + singletonMap("vec", singletonMap("1", 100)), // empty doc emptyMap() }; List reqs = new ArrayList<>(); @@ -105,6 +112,7 @@ public void testExists() throws Exception { expected.put("bar.bar", 1); expected.put("bar.bar.bar", 1); expected.put("foobar", 0); + expected.put("vec", 2); final long numDocs = sources.length; SearchResponse allDocs = client().prepareSearch("idx").setSize(sources.length).get(); diff --git a/server/src/main/java/org/elasticsearch/index/IndexVersion.java b/server/src/main/java/org/elasticsearch/index/IndexVersion.java index 1cb03574afd86..49a98a47dcbea 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexVersion.java +++ b/server/src/main/java/org/elasticsearch/index/IndexVersion.java @@ -125,6 +125,7 @@ private static IndexVersion registerIndexVersion(int id, Version luceneVersion, */ public static final IndexVersion V_8_500_000 = registerIndexVersion(8_500_000, Version.LUCENE_9_7_0, "bf656f5e-5808-4eee-bf8a-e2bf6736ff55"); public static final IndexVersion V_8_500_001 = registerIndexVersion(8_500_001, Version.LUCENE_9_7_0, "45045a5a-fc57-4462-89f6-6bc04cda6015"); + public static final IndexVersion V_8_500_002 = registerIndexVersion(8_500_002, Version.LUCENE_9_7_0, "50b39bf8-6c6a-443e-a5e5-069438d843c1"); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java index 758ad8e508ea3..eded63e12e758 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java @@ -46,6 +46,7 @@ public class SparseVectorFieldMapper extends FieldMapper { static final IndexVersion PREVIOUS_SPARSE_VECTOR_INDEX_VERSION = IndexVersion.V_8_0_0; static final IndexVersion NEW_SPARSE_VECTOR_INDEX_VERSION = IndexVersion.V_8_500_001; + static final IndexVersion SPARSE_VECTOR_IN_FIELD_NAMES_INDEX_VERSION = IndexVersion.V_8_500_002; public static class Builder extends FieldMapper.Builder { @@ -92,11 +93,6 @@ public String typeName() { return CONTENT_TYPE; } - @Override - public Query existsQuery(SearchExecutionContext context) { - throw new IllegalArgumentException("[sparse_vector] fields do not support [exists] queries"); - } - @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { throw new IllegalArgumentException("[sparse_vector] fields do not support sorting, scripting or aggregating"); @@ -112,6 +108,15 @@ public Query termQuery(Object value, SearchExecutionContext context) { return FeatureField.newLinearQuery(name(), indexedValueForSearch(value), DEFAULT_BOOST); } + @Override + public Query existsQuery(SearchExecutionContext context) { + // No support for exists queries prior to this version + if (context.getIndexSettings().getIndexVersionCreated().before(SPARSE_VECTOR_IN_FIELD_NAMES_INDEX_VERSION)) { + throw new IllegalArgumentException("[sparse_vector] fields do not support [exists] queries"); + } + return super.existsQuery(context); + } + private static String indexedValueForSearch(Object value) { if (value instanceof BytesRef) { return ((BytesRef) value).utf8ToString(); @@ -193,6 +198,9 @@ public void parse(DocumentParserContext context) throws IOException { ); } } + if (context.indexSettings().getIndexVersionCreated().onOrAfter(SPARSE_VECTOR_IN_FIELD_NAMES_INDEX_VERSION)) { + context.addToFieldNames(fieldType().name()); + } } finally { context.path().setWithinLeafObject(false); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java index 9ea63325ef3ad..dfcd48b1ca3fd 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java @@ -19,7 +19,6 @@ import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperTestCase; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; @@ -51,12 +50,6 @@ protected Object getSampleObjectForDocument() { return getSampleValueForDocument(); } - @Override - protected void assertExistsQuery(MapperService mapperService) { - IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> super.assertExistsQuery(mapperService)); - assertEquals("[sparse_vector] fields do not support [exists] queries", iae.getMessage()); - } - @Override protected void minimalMapping(XContentBuilder b) throws IOException { b.field("type", "sparse_vector"); From b023061dbad4196f99131267ed82cee4d4ffb386 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 28 Sep 2023 10:12:25 +0200 Subject: [PATCH 115/155] Force-merge all test indices at once (#99957) This disables waiting for each test index to become search-available and force-merges all of them in one go, making the data loading slightly faster. --- .../xpack/esql/CsvTestsDataLoader.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestsDataLoader.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestsDataLoader.java index 5a8e269ac6bc3..ed53b4bac839b 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestsDataLoader.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestsDataLoader.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import static org.elasticsearch.common.Strings.delimitedListToStringArray; import static org.elasticsearch.common.logging.LoggerMessageFormat.format; @@ -135,6 +136,7 @@ public static void loadDataSetIntoEs(RestClient client, Logger logger) throws IO for (var dataSet : CSV_DATASET_MAP.values()) { load(client, dataSet.indexName, "/" + dataSet.mappingFileName, "/" + dataSet.dataFileName, logger); } + forceMerge(client, CSV_DATASET_MAP.keySet(), logger); for (var policy : ENRICH_POLICIES) { loadEnrichPolicy(client, policy.policyName, policy.policyFileName, logger); } @@ -216,9 +218,9 @@ private static void loadCsvData( ); } else { name = entries[i].substring(0, split).trim(); - if (name.indexOf(".") < 0) { + if (name.contains(".") == false) { typeName = entries[i].substring(split + 1).trim(); - if (typeName.length() == 0) { + if (typeName.isEmpty()) { throw new IllegalArgumentException( "A type is always expected in the schema definition; found " + entries[i] ); @@ -300,7 +302,7 @@ private static void loadCsvData( } request.setJsonEntity(builder.toString()); - request.addParameter("refresh", "wait_for"); + request.addParameter("refresh", "false"); // will be _forcemerge'd next Response response = client.performRequest(request); if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); @@ -309,20 +311,25 @@ private static void loadCsvData( Map result = XContentHelper.convertToMap(xContentType.xContent(), content, false); Object errors = result.get("errors"); if (Boolean.FALSE.equals(errors)) { - logger.info("Data loading OK"); - request = new Request("POST", "/" + indexName + "/_forcemerge?max_num_segments=1"); - response = client.performRequest(request); - if (response.getStatusLine().getStatusCode() != 200) { - logger.warn("Force-merge to 1 segment failed: " + response.getStatusLine()); - } else { - logger.info("Forced-merge to 1 segment"); - } + logger.info("Data loading of [{}] OK", indexName); } else { - logger.error("Data loading FAILED"); + throw new IOException("Data loading of [" + indexName + "] failed with errors: " + errors); } } } else { - logger.error("Error loading data: " + response.getStatusLine()); + throw new IOException("Data loading of [" + indexName + "] failed with status: " + response.getStatusLine()); + } + } + + private static void forceMerge(RestClient client, Set indices, Logger logger) throws IOException { + String pattern = String.join(",", indices); + + Request request = new Request("POST", "/" + pattern + "/_forcemerge?max_num_segments=1"); + Response response = client.performRequest(request); + if (response.getStatusLine().getStatusCode() != 200) { + logger.warn("Force-merging [{}] to 1 segment failed: {}", pattern, response.getStatusLine()); + } else { + logger.info("[{}] forced-merged to 1 segment", pattern); } } From fd93bd526daeec346ffde1fe245857de7268dc5c Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Thu, 28 Sep 2023 10:26:32 +0200 Subject: [PATCH 116/155] [Profiling] Insert root node in empty flamegraph (#99993) With this commit we ensure that a root node is always present in the structure returned by the flamegraph API. --- .../profiling/GetFlamegraphResponse.java | 40 ++++++++++ .../TransportGetFlamegraphAction.java | 8 +- .../TransportGetFlamegraphActionTests.java | 74 +++++++++++++++++++ 3 files changed, 117 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java index 0ab9060aa8936..c006b52c5ed27 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java @@ -119,6 +119,46 @@ public List getCountExclusive() { return countExclusive; } + public List> getEdges() { + return edges; + } + + public List getFileIds() { + return fileIds; + } + + public List getFrameTypes() { + return frameTypes; + } + + public List getInlineFrames() { + return inlineFrames; + } + + public List getFileNames() { + return fileNames; + } + + public List getAddressOrLines() { + return addressOrLines; + } + + public List getFunctionNames() { + return functionNames; + } + + public List getFunctionOffsets() { + return functionOffsets; + } + + public List getSourceFileNames() { + return sourceFileNames; + } + + public List getSourceLines() { + return sourceLines; + } + @Override public Iterator toXContentChunked(ToXContent.Params params) { return Iterators.concat( diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java index acef2f6661c02..e11821a5a86b7 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java @@ -190,11 +190,9 @@ private static class FlamegraphBuilder { this.sourceLines = new ArrayList<>(capacity); this.countInclusive = new ArrayList<>(capacity); this.countExclusive = new ArrayList<>(capacity); - if (frames > 0) { - // root node - int nodeId = this.addNode("", 0, false, "", 0, "", 0, "", 0, 0, null); - this.setCurrentNode(nodeId); - } + // always insert root node + int nodeId = this.addNode("", 0, false, "", 0, "", 0, "", 0, 0, null); + this.setCurrentNode(nodeId); this.samplingRate = samplingRate; } diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java index 2adb41ce45038..d931e4e17d5b4 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java @@ -56,6 +56,80 @@ public void testCreateFlamegraph() { assertEquals(1.0d, response.getSamplingRate(), 0.001d); assertEquals(List.of(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), response.getCountInclusive()); assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 1), response.getCountExclusive()); + assertEquals( + List.of( + Map.of("fr28zxcZ2UDasxYuu6dV-w12784352", 1), + Map.of("fr28zxcZ2UDasxYuu6dV-w19334053", 2), + Map.of("fr28zxcZ2UDasxYuu6dV-w19336161", 3), + Map.of("fr28zxcZ2UDasxYuu6dV-w18795859", 4), + Map.of("fr28zxcZ2UDasxYuu6dV-w18622708", 5), + Map.of("fr28zxcZ2UDasxYuu6dV-w18619213", 6), + Map.of("fr28zxcZ2UDasxYuu6dV-w12989721", 7), + Map.of("fr28zxcZ2UDasxYuu6dV-w13658842", 8), + Map.of("fr28zxcZ2UDasxYuu6dV-w16339645", 9), + Map.of() + ), + response.getEdges() + ); + assertEquals( + List.of( + "", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w", + "fr28zxcZ2UDasxYuu6dV-w" + ), + response.getFileIds() + ); + assertEquals(List.of(0, 3, 3, 3, 3, 3, 3, 3, 3, 3), response.getFrameTypes()); + assertEquals(List.of(false, false, false, false, false, false, false, false, false, false), response.getInlineFrames()); + assertEquals( + List.of( + "", + "containerd", + "containerd", + "containerd", + "containerd", + "containerd", + "containerd", + "containerd", + "containerd", + "containerd" + ), + response.getFileNames() + ); + assertEquals( + List.of(0, 12784352, 19334053, 19336161, 18795859, 18622708, 18619213, 12989721, 13658842, 16339645), + response.getAddressOrLines() + ); + assertEquals(List.of("", "", "", "", "", "", "", "", "", ""), response.getFunctionNames()); + assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0), response.getFunctionOffsets()); + assertEquals(List.of("", "", "", "", "", "", "", "", "", ""), response.getSourceFileNames()); + assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0), response.getSourceLines()); + } + public void testCreateEmptyFlamegraphWithRootNode() { + GetStackTracesResponse stacktraces = new GetStackTracesResponse(Map.of(), Map.of(), Map.of(), Map.of(), 0, 1.0d); + GetFlamegraphResponse response = TransportGetFlamegraphAction.buildFlamegraph(stacktraces); + assertNotNull(response); + assertEquals(1, response.getSize()); + assertEquals(1.0d, response.getSamplingRate(), 0.001d); + assertEquals(List.of(0), response.getCountInclusive()); + assertEquals(List.of(0), response.getCountExclusive()); + assertEquals(List.of(Map.of()), response.getEdges()); + assertEquals(List.of(""), response.getFileIds()); + assertEquals(List.of(0), response.getFrameTypes()); + assertEquals(List.of(false), response.getInlineFrames()); + assertEquals(List.of(""), response.getFileNames()); + assertEquals(List.of(0), response.getAddressOrLines()); + assertEquals(List.of(""), response.getFunctionNames()); + assertEquals(List.of(0), response.getFunctionOffsets()); + assertEquals(List.of(""), response.getSourceFileNames()); + assertEquals(List.of(0), response.getSourceLines()); } } From 56964d520ac51914455a498164b32ca7a1d0265b Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 28 Sep 2023 09:29:36 +0100 Subject: [PATCH 117/155] [Transform] Remove usages of Version from transforms (#99974) Transforms code outside of TransformConfigVersion no longer uses the Version class. --- .../TransformDestIndexSettingsTests.java | 4 ++-- .../integration/TransformDestIndexIT.java | 4 ++-- .../transform/persistence/TransformIndex.java | 11 ++++++---- .../persistence/TransformInternalIndex.java | 20 ++++++++++++++++--- ...TransformPersistentTasksExecutorTests.java | 2 -- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformDestIndexSettingsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformDestIndexSettingsTests.java index 0506500c654b6..82833d894f0ce 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformDestIndexSettingsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/TransformDestIndexSettingsTests.java @@ -7,13 +7,13 @@ package org.elasticsearch.xpack.core.transform.transforms; -import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.Maps; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.transform.AbstractSerializingTransformTestCase; +import org.elasticsearch.xpack.core.transform.TransformConfigVersion; import java.io.IOException; import java.util.HashSet; @@ -31,7 +31,7 @@ public static TransformDestIndexSettings randomDestIndexSettings() { if (randomBoolean()) { mappings = Maps.newMapWithExpectedSize(size); - mappings.put("_meta", singletonMap("_transform", singletonMap("version", Version.CURRENT.toString()))); + mappings.put("_meta", singletonMap("_transform", singletonMap("version", TransformConfigVersion.CURRENT.toString()))); for (int i = 0; i < size; i++) { mappings.put(randomAlphaOfLength(10), singletonMap("type", randomAlphaOfLength(10))); } diff --git a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDestIndexIT.java b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDestIndexIT.java index e0322136061dd..5e3ec897f72b7 100644 --- a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDestIndexIT.java +++ b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformDestIndexIT.java @@ -7,10 +7,10 @@ package org.elasticsearch.xpack.transform.integration; -import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.xpack.core.transform.TransformConfigVersion; import org.elasticsearch.xpack.core.transform.transforms.DestAlias; import org.elasticsearch.xpack.core.transform.transforms.SettingsConfig; import org.junit.Before; @@ -53,7 +53,7 @@ public void testTransformDestIndexMetadata() throws Exception { Map mappingAsMap = entityAsMap(mappingResponse); assertEquals( - Version.CURRENT.toString(), + TransformConfigVersion.CURRENT.toString(), XContentMapValues.extractValue("pivot_reviews.mappings._meta._transform.version.created", mappingAsMap) ); assertTrue( diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformIndex.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformIndex.java index 39183a1ca8502..86d03ca37fc57 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformIndex.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformIndex.java @@ -10,7 +10,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.ResourceAlreadyExistsException; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction; @@ -28,6 +27,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.xpack.core.ClientHelper; +import org.elasticsearch.xpack.core.transform.TransformConfigVersion; import org.elasticsearch.xpack.core.transform.TransformField; import org.elasticsearch.xpack.core.transform.TransformMessages; import org.elasticsearch.xpack.core.transform.transforms.DestAlias; @@ -265,14 +265,14 @@ public static TransformDestIndexSettings createTransformDestIndexSettings( } /* - * Return meta data that stores some useful information about the transform index, stored as "_meta": + * Return metadata that stores some useful information about the transform index, stored as "_meta": * * { * "created_by" : "transform", * "_transform" : { * "transform" : "id", * "version" : { - * "created" : "8.0.0" + * "created" : "10.0.0" * }, * "creation_date_in_millis" : 1584025695202 * } @@ -285,7 +285,10 @@ private static Map createMetadata(String id, Clock clock) { Map transformMetadata = new HashMap<>(); transformMetadata.put(TransformField.CREATION_DATE_MILLIS, clock.millis()); - transformMetadata.put(TransformField.VERSION.getPreferredName(), Map.of(TransformField.CREATED, Version.CURRENT.toString())); + transformMetadata.put( + TransformField.VERSION.getPreferredName(), + Map.of(TransformField.CREATED, TransformConfigVersion.CURRENT.toString()) + ); transformMetadata.put(TransformField.TRANSFORM, id); metadata.put(TransformField.META_FIELDNAME, transformMetadata); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformInternalIndex.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformInternalIndex.java index 722f37059cc56..c580417c578fe 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformInternalIndex.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/persistence/TransformInternalIndex.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.transform.persistence; import org.elasticsearch.ResourceAlreadyExistsException; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -69,6 +68,22 @@ public final class TransformInternalIndex { * version 7 (7.13):add mapping for config::pivot, config::latest, config::retention_policy and config::sync */ + /** + * The new system index mappings version used in preference to version + * since 8.10. A value of 1 for this constant corresponds to version 7 in the table + * of changes above. Increment this constant by one at the same time as adding a new + * entry to the table of changes above. + */ + public static final int TRANSFORM_INDEX_MAPPINGS_VERSION = 1; + /** + * No longer used for determining the age of mappings, but system index descriptor + * code requires something be set. We use a value that can be parsed by + * old nodes in mixed-version clusters, just in case any old code exists that + * tries to parse version from index metadata, and that will indicate + * to these old nodes that the mappings are newer than they are. + */ + private static final String LEGACY_VERSION_FIELD_VALUE = "8.11.0"; + // constants for mappings public static final String DYNAMIC = "dynamic"; public static final String PROPERTIES = "properties"; @@ -87,7 +102,6 @@ public final class TransformInternalIndex { public static final String KEYWORD = "keyword"; public static final String BOOLEAN = "boolean"; public static final String FLATTENED = "flattened"; - public static final int TRANSFORM_INDEX_MAPPINGS_VERSION = 1; public static SystemIndexDescriptor getSystemIndexDescriptor(Settings transformInternalIndexAdditionalSettings) throws IOException { return SystemIndexDescriptor.builder() @@ -359,7 +373,7 @@ private static XContentBuilder addTransformCheckpointMappings(XContentBuilder bu */ private static XContentBuilder addMetaInformation(XContentBuilder builder) throws IOException { return builder.startObject("_meta") - .field("version", Version.CURRENT) + .field("version", LEGACY_VERSION_FIELD_VALUE) .field(SystemIndexDescriptor.VERSION_META_KEY, TRANSFORM_INDEX_MAPPINGS_VERSION) .endObject(); } diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutorTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutorTests.java index 7a8ae7f5e6aa4..69f4a66b53f7c 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutorTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutorTests.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.transform.transforms; -import org.elasticsearch.Version; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -323,7 +322,6 @@ private DiscoveryNodes.Builder buildNodes( DiscoveryNodeRole.TRANSFORM_ROLE ) ) - .version(Version.V_7_7_0) .attributes( Map.of(TransformConfigVersion.TRANSFORM_CONFIG_VERSION_NODE_ATTR, TransformConfigVersion.V_7_7_0.toString()) ) From 0ef8232da92adf513ad2542c7fad1791d10712a3 Mon Sep 17 00:00:00 2001 From: Mary Gouseti Date: Thu, 28 Sep 2023 11:45:32 +0300 Subject: [PATCH 118/155] Replace 200_logs_datastream_defaults.yml with java rest test (#99979) --- .../datastreams/LogsDataStreamIT.java | 169 ++++++++++++++++++ .../200_logs_datastream_defaults.yml | 108 ----------- 2 files changed, 169 insertions(+), 108 deletions(-) create mode 100644 modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java delete mode 100644 modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/200_logs_datastream_defaults.yml diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java new file mode 100644 index 0000000000000..31d2f6a8e2171 --- /dev/null +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.datastreams; + +import org.elasticsearch.client.Request; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.client.RestClient; +import org.junit.After; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +public class LogsDataStreamIT extends DisabledSecurityDataStreamTestCase { + + @After + public void cleanUp() throws IOException { + adminClient().performRequest(new Request("DELETE", "_data_stream/*")); + } + + @SuppressWarnings("unchecked") + public void testDefaultLogsSettingAndMapping() throws Exception { + RestClient client = client(); + waitForLogs(client); + + String dataStreamName = "logs-generic-default"; + createDataStream(client, dataStreamName); + String backingIndex = getWriteBackingIndex(client, dataStreamName); + + // Ensure correct settings + Map settings = getSettings(client, backingIndex); + assertThat(settings.get("index.mapping.ignore_malformed"), is("true")); + + // Extend the mapping and verify + putMapping(client, backingIndex); + Map mappingProperties = getMappingProperties(client, backingIndex); + assertThat(((Map) mappingProperties.get("@timestamp")).get("ignore_malformed"), equalTo(false)); + assertThat(((Map) mappingProperties.get("numeric_field")).get("type"), equalTo("integer")); + + // Insert valid doc and verify successful indexing + { + indexDoc(client, dataStreamName, """ + { + "@timestamp": "2023-04-18", + "message": "valid", + "numeric_field": 42 + } + """); + List results = searchDocs(client, dataStreamName, """ + { + "query": { + "term": { + "message": { + "value": "valid" + } + } + }, + "fields": ["numeric_field"] + } + """); + Map fields = ((Map>) results.get(0)).get("fields"); + assertThat(fields.get("numeric_field"), is(List.of(42))); + } + + // Insert invalid doc and verify successful indexing + { + indexDoc(client, dataStreamName, """ + { + "@timestamp": "2023-04-18", + "message": "invalid", + "numeric_field": "forty-two" + } + """); + List results = searchDocs(client, dataStreamName, """ + { + "query": { + "term": { + "message": { + "value": "invalid" + } + } + }, + "fields": ["numeric_field"] + } + """); + List ignored = ((Map>) results.get(0)).get("_ignored"); + assertThat(ignored, contains("numeric_field")); + Map ignoredFieldValues = ((Map>) results.get(0)).get("ignored_field_values"); + assertThat(ignoredFieldValues.get("numeric_field"), is(List.of("forty-two"))); + } + } + + private static void waitForLogs(RestClient client) throws Exception { + assertBusy(() -> { + try { + Request request = new Request("GET", "_index_template/logs"); + assertOK(client.performRequest(request)); + } catch (ResponseException e) { + fail(e.getMessage()); + } + }); + } + + private static void createDataStream(RestClient client, String name) throws IOException { + Request request = new Request("PUT", "_data_stream/" + name); + assertOK(client.performRequest(request)); + } + + @SuppressWarnings("unchecked") + private static String getWriteBackingIndex(RestClient client, String name) throws IOException { + Request request = new Request("GET", "_data_stream/" + name); + List dataStreams = (List) entityAsMap(client.performRequest(request)).get("data_streams"); + Map dataStream = (Map) dataStreams.get(0); + List> indices = (List>) dataStream.get("indices"); + return indices.get(0).get("index_name"); + } + + @SuppressWarnings("unchecked") + private static Map getSettings(RestClient client, String indexName) throws IOException { + Request request = new Request("GET", "/" + indexName + "/_settings?flat_settings"); + return ((Map>) entityAsMap(client.performRequest(request)).get(indexName)).get("settings"); + } + + private static void putMapping(RestClient client, String indexName) throws IOException { + Request request = new Request("PUT", "/" + indexName + "/_mapping"); + request.setJsonEntity(""" + { + "properties": { + "numeric_field": { + "type": "integer" + } + } + } + """); + assertOK(client.performRequest(request)); + } + + @SuppressWarnings("unchecked") + private static Map getMappingProperties(RestClient client, String indexName) throws IOException { + Request request = new Request("GET", "/" + indexName + "/_mapping"); + Map map = (Map) entityAsMap(client.performRequest(request)).get(indexName); + Map mappings = (Map) map.get("mappings"); + return (Map) mappings.get("properties"); + } + + private static void indexDoc(RestClient client, String dataStreamName, String doc) throws IOException { + Request request = new Request("POST", "/" + dataStreamName + "/_doc?refresh=true"); + request.setJsonEntity(doc); + assertOK(client.performRequest(request)); + } + + @SuppressWarnings("unchecked") + private static List searchDocs(RestClient client, String dataStreamName, String query) throws IOException { + Request request = new Request("GET", "/" + dataStreamName + "/_search"); + request.setJsonEntity(query); + Map hits = (Map) entityAsMap(client.performRequest(request)).get("hits"); + return (List) hits.get("hits"); + } +} diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/200_logs_datastream_defaults.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/200_logs_datastream_defaults.yml deleted file mode 100644 index eaaf0893c1a83..0000000000000 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/200_logs_datastream_defaults.yml +++ /dev/null @@ -1,108 +0,0 @@ ---- -Verify default logs-*-* settings and mappings: - - do: - indices.create_data_stream: - name: logs-generic-default - - is_true: acknowledged - - - do: - indices.get_data_stream: - name: logs-generic-default - - set: { data_streams.0.indices.0.index_name: idx0name } - - # default backing index settings should be "ignore_malformed": true - - do: - indices.get_settings: - index: $idx0name - - match: { .$idx0name.settings.index.mapping.ignore_malformed: "true" } - - # add test field mapping - - do: - indices.put_mapping: - index: $idx0name - body: - properties: - numeric_field: - type: integer - - is_true: acknowledged - - # default backing index mapping should contain an exception for the @timestamp field - "ignore_malformed": false - - do: - indices.get_mapping: - index: $idx0name - - match: { .$idx0name.mappings.properties.@timestamp.ignore_malformed: false } - - match: { .$idx0name.mappings.properties.numeric_field.type: "integer" } - - - do: - index: - index: logs-generic-default - refresh: true - body: - '@timestamp': '2023-04-18' - message: 'valid' - numeric_field: 42 - - match: {result: "created"} - - - do: - search: - index: logs-generic-default - body: - query: - term: - message: - value: 'valid' - fields: - - field: 'numeric_field' - - length: { hits.hits: 1 } - - length: { hits.hits.0.fields: 1 } - - match: { hits.hits.0.fields.numeric_field.0: 42 } - - - do: - index: - index: logs-generic-default - refresh: true - body: - '@timestamp': '2023-04-18' - message: 'number_as_string' - numeric_field: "42" - - match: {result: "created"} - - - do: - search: - index: logs-generic-default - body: - query: - term: - message: - value: 'number_as_string' - fields: - - field: 'numeric_field' - - length: { hits.hits: 1 } - - length: { hits.hits.0.fields: 1 } - - match: { hits.hits.0.fields.numeric_field.0: 42 } - - - do: - index: - index: logs-generic-default - refresh: true - body: - '@timestamp': '2023-04-18' - message: 'invalid' - numeric_field: "forty-two" - - match: {result: "created"} - - - do: - search: - index: logs-generic-default - body: - query: - term: - message: - value: 'invalid' - fields: - - field: 'numeric_field' - - length: { hits.hits: 1 } - - length: { hits.hits.0._ignored: 1 } - - match: { hits.hits.0._ignored.0: 'numeric_field' } - - length: { hits.hits.0.ignored_field_values.numeric_field: 1 } - - match: { hits.hits.0.ignored_field_values.numeric_field.0: 'forty-two' } From d106fece41feb58c5f224c9eb52ec90f2de7755a Mon Sep 17 00:00:00 2001 From: Mary Gouseti Date: Thu, 28 Sep 2023 11:45:51 +0300 Subject: [PATCH 119/155] Replace the disabled stack templates yaml test with java rest test (#99978) --- .../xpack/core/StackTemplatesRestIT.java | 59 +++++++++++++++++++ .../rest-api-spec/test/stack/10_stack.yml | 22 ------- 2 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugin/core/src/javaRestTest/java/org/elasticsearch/xpack/core/StackTemplatesRestIT.java delete mode 100644 x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/resources/rest-api-spec/test/stack/10_stack.yml diff --git a/x-pack/plugin/core/src/javaRestTest/java/org/elasticsearch/xpack/core/StackTemplatesRestIT.java b/x-pack/plugin/core/src/javaRestTest/java/org/elasticsearch/xpack/core/StackTemplatesRestIT.java new file mode 100644 index 0000000000000..fcbf955c2b9ae --- /dev/null +++ b/x-pack/plugin/core/src/javaRestTest/java/org/elasticsearch/xpack/core/StackTemplatesRestIT.java @@ -0,0 +1,59 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.core; + +import org.elasticsearch.client.Request; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.test.rest.ESRestTestCase; + +import static org.hamcrest.Matchers.is; + +public class StackTemplatesRestIT extends ESRestTestCase { + + private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("x_pack_rest_user", new SecureString("x-pack-test-password")); + + @Override + protected Settings restClientSettings() { + return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE).build(); + } + + public void testTemplatesCanBeDisabled() throws Exception { + RestClient client = client(); + // Ensure the logs template has been added + assertBusy(() -> { + try { + Request request = new Request("GET", "_index_template/logs"); + assertOK(client.performRequest(request)); + } catch (ResponseException e) { + fail(e.getMessage()); + } + }); + // Disable the stack templates + { + Request request = new Request("PUT", "_cluster/settings"); + request.setJsonEntity(""" + { + "persistent": { + "stack.templates.enabled": false + } + } + """); + assertOK(client.performRequest(request)); + } + Request getRequest = new Request("GET", "_index_template/logs"); + assertOK(client.performRequest(getRequest)); + + Request deleteRequest = new Request("DELETE", "_index_template/logs"); + assertOK(client.performRequest(deleteRequest)); + ResponseException exception = expectThrows(ResponseException.class, () -> client.performRequest(deleteRequest)); + assertThat(exception.getResponse().getStatusLine().getStatusCode(), is(404)); + } +} diff --git a/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/resources/rest-api-spec/test/stack/10_stack.yml b/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/resources/rest-api-spec/test/stack/10_stack.yml deleted file mode 100644 index 3346f5c8e58bd..0000000000000 --- a/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/resources/rest-api-spec/test/stack/10_stack.yml +++ /dev/null @@ -1,22 +0,0 @@ -"Stack templates can be disabled": - - skip: - version: all - reason: https://github.com/elastic/elasticsearch/issues/98163 - - do: - cluster.put_settings: - body: - persistent: - stack.templates.enabled: false - - - do: - indices.get_index_template: - name: logs - - - do: - indices.delete_index_template: - name: logs - - - do: - catch: missing - indices.get_index_template: - name: logs From a698f4dce22f95ff234ff67ca04a3905fd9906a5 Mon Sep 17 00:00:00 2001 From: Jaime Pan <33685703+NEUpanning@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:54:55 +0800 Subject: [PATCH 120/155] Extract NodeInfoMetrics to top-level class (#99990) --- .../cluster/node/info/NodesInfoMetrics.java | 75 +++++++++++++++++++ .../cluster/node/info/NodesInfoRequest.java | 66 +++++----------- .../node/info/NodesInfoRequestBuilder.java | 22 +++--- .../node/info/TransportNodesInfoAction.java | 24 +++--- .../remote/RemoteClusterNodesAction.java | 3 +- .../ingest/PutPipelineTransportAction.java | 3 +- .../admin/cluster/RestNodesInfoAction.java | 3 +- .../rest/action/cat/RestNodeAttrsAction.java | 3 +- .../rest/action/cat/RestNodesAction.java | 9 ++- .../rest/action/cat/RestPluginsAction.java | 3 +- .../rest/action/cat/RestThreadPoolAction.java | 3 +- .../node/info/NodesInfoRequestTests.java | 12 +-- .../remote/RemoteClusterNodesActionTests.java | 3 +- .../nodeinfo/AutoscalingNodeInfoService.java | 3 +- .../TransportNodeEnrollmentAction.java | 3 +- .../InternalEnrollmentTokenGenerator.java | 5 +- 16 files changed, 147 insertions(+), 93 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoMetrics.java diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoMetrics.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoMetrics.java new file mode 100644 index 0000000000000..3e632f9bdd212 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoMetrics.java @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.cluster.node.info; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This class is a container that encapsulates the necessary information needed to indicate which node information is requested. + */ +public class NodesInfoMetrics implements Writeable { + private Set requestedMetrics = Metric.allMetrics(); + + public NodesInfoMetrics() {} + + public NodesInfoMetrics(StreamInput in) throws IOException { + requestedMetrics.clear(); + requestedMetrics.addAll(Arrays.asList(in.readStringArray())); + } + + public Set requestedMetrics() { + return requestedMetrics; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeStringArray(requestedMetrics.toArray(new String[0])); + } + + /** + * An enumeration of the "core" sections of metrics that may be requested + * from the nodes information endpoint. Eventually this list will be + * pluggable. + */ + public enum Metric { + SETTINGS("settings"), + OS("os"), + PROCESS("process"), + JVM("jvm"), + THREAD_POOL("thread_pool"), + TRANSPORT("transport"), + HTTP("http"), + REMOTE_CLUSTER_SERVER("remote_cluster_server"), + PLUGINS("plugins"), + INGEST("ingest"), + AGGREGATIONS("aggregations"), + INDICES("indices"); + + private final String metricName; + + Metric(String name) { + this.metricName = name; + } + + public String metricName() { + return this.metricName; + } + + public static Set allMetrics() { + return Arrays.stream(values()).map(Metric::metricName).collect(Collectors.toSet()); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java index 4e52116020be2..04b2a7d980678 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequest.java @@ -13,18 +13,16 @@ import org.elasticsearch.common.io.stream.StreamOutput; import java.io.IOException; -import java.util.Arrays; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.stream.Collectors; /** * A request to get node (cluster) level information. */ public class NodesInfoRequest extends BaseNodesRequest { - private Set requestedMetrics = Metric.allMetrics(); + private NodesInfoMetrics nodesInfoMetrics; /** * Create a new NodeInfoRequest from a {@link StreamInput} object. @@ -34,8 +32,7 @@ public class NodesInfoRequest extends BaseNodesRequest { */ public NodesInfoRequest(StreamInput in) throws IOException { super(in); - requestedMetrics.clear(); - requestedMetrics.addAll(Arrays.asList(in.readStringArray())); + nodesInfoMetrics = new NodesInfoMetrics(in); } /** @@ -45,6 +42,7 @@ public NodesInfoRequest(StreamInput in) throws IOException { @SuppressWarnings("this-escape") public NodesInfoRequest(String... nodesIds) { super(nodesIds); + nodesInfoMetrics = new NodesInfoMetrics(); all(); } @@ -52,7 +50,7 @@ public NodesInfoRequest(String... nodesIds) { * Clears all info flags. */ public NodesInfoRequest clear() { - requestedMetrics.clear(); + nodesInfoMetrics.requestedMetrics().clear(); return this; } @@ -60,7 +58,7 @@ public NodesInfoRequest clear() { * Sets to return all the data. */ public NodesInfoRequest all() { - requestedMetrics.addAll(Metric.allMetrics()); + nodesInfoMetrics.requestedMetrics().addAll(NodesInfoMetrics.Metric.allMetrics()); return this; } @@ -68,17 +66,17 @@ public NodesInfoRequest all() { * Get the names of requested metrics */ public Set requestedMetrics() { - return Set.copyOf(requestedMetrics); + return Set.copyOf(nodesInfoMetrics.requestedMetrics()); } /** * Add metric */ public NodesInfoRequest addMetric(String metric) { - if (Metric.allMetrics().contains(metric) == false) { + if (NodesInfoMetrics.Metric.allMetrics().contains(metric) == false) { throw new IllegalStateException("Used an illegal metric: " + metric); } - requestedMetrics.add(metric); + nodesInfoMetrics.requestedMetrics().add(metric); return this; } @@ -87,12 +85,12 @@ public NodesInfoRequest addMetric(String metric) { */ public NodesInfoRequest addMetrics(String... metrics) { SortedSet metricsSet = new TreeSet<>(Set.of(metrics)); - if (Metric.allMetrics().containsAll(metricsSet) == false) { - metricsSet.removeAll(Metric.allMetrics()); + if (NodesInfoMetrics.Metric.allMetrics().containsAll(metricsSet) == false) { + metricsSet.removeAll(NodesInfoMetrics.Metric.allMetrics()); String plural = metricsSet.size() == 1 ? "" : "s"; throw new IllegalStateException("Used illegal metric" + plural + ": " + metricsSet); } - requestedMetrics.addAll(metricsSet); + nodesInfoMetrics.requestedMetrics().addAll(metricsSet); return this; } @@ -100,17 +98,17 @@ public NodesInfoRequest addMetrics(String... metrics) { * Remove metric */ public NodesInfoRequest removeMetric(String metric) { - if (Metric.allMetrics().contains(metric) == false) { + if (NodesInfoMetrics.Metric.allMetrics().contains(metric) == false) { throw new IllegalStateException("Used an illegal metric: " + metric); } - requestedMetrics.remove(metric); + nodesInfoMetrics.requestedMetrics().remove(metric); return this; } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeStringCollection(requestedMetrics); + nodesInfoMetrics.writeTo(out); } /** @@ -118,7 +116,7 @@ public void writeTo(StreamOutput out) throws IOException { * @param metrics the metrics to include in the request * @return */ - public static NodesInfoRequest requestWithMetrics(Metric... metrics) { + public static NodesInfoRequest requestWithMetrics(NodesInfoMetrics.Metric... metrics) { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); nodesInfoRequest.clear(); for (var metric : metrics) { @@ -127,37 +125,7 @@ public static NodesInfoRequest requestWithMetrics(Metric... metrics) { return nodesInfoRequest; } - /** - * An enumeration of the "core" sections of metrics that may be requested - * from the nodes information endpoint. Eventually this list list will be - * pluggable. - */ - public enum Metric { - SETTINGS("settings"), - OS("os"), - PROCESS("process"), - JVM("jvm"), - THREAD_POOL("thread_pool"), - TRANSPORT("transport"), - HTTP("http"), - REMOTE_CLUSTER_SERVER("remote_cluster_server"), - PLUGINS("plugins"), - INGEST("ingest"), - AGGREGATIONS("aggregations"), - INDICES("indices"); - - private final String metricName; - - Metric(String name) { - this.metricName = name; - } - - public String metricName() { - return this.metricName; - } - - public static Set allMetrics() { - return Arrays.stream(values()).map(Metric::metricName).collect(Collectors.toSet()); - } + public NodesInfoMetrics getNodesInfoMetrics() { + return nodesInfoMetrics; } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java index e839553021c3e..543804869eaa0 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java @@ -38,7 +38,7 @@ public NodesInfoRequestBuilder all() { * Should the node settings be returned. */ public NodesInfoRequestBuilder setSettings(boolean settings) { - addOrRemoveMetric(settings, NodesInfoRequest.Metric.SETTINGS); + addOrRemoveMetric(settings, NodesInfoMetrics.Metric.SETTINGS); return this; } @@ -46,7 +46,7 @@ public NodesInfoRequestBuilder setSettings(boolean settings) { * Should the node OS info be returned. */ public NodesInfoRequestBuilder setOs(boolean os) { - addOrRemoveMetric(os, NodesInfoRequest.Metric.OS); + addOrRemoveMetric(os, NodesInfoMetrics.Metric.OS); return this; } @@ -54,7 +54,7 @@ public NodesInfoRequestBuilder setOs(boolean os) { * Should the node OS process be returned. */ public NodesInfoRequestBuilder setProcess(boolean process) { - addOrRemoveMetric(process, NodesInfoRequest.Metric.PROCESS); + addOrRemoveMetric(process, NodesInfoMetrics.Metric.PROCESS); return this; } @@ -62,7 +62,7 @@ public NodesInfoRequestBuilder setProcess(boolean process) { * Should the node JVM info be returned. */ public NodesInfoRequestBuilder setJvm(boolean jvm) { - addOrRemoveMetric(jvm, NodesInfoRequest.Metric.JVM); + addOrRemoveMetric(jvm, NodesInfoMetrics.Metric.JVM); return this; } @@ -70,7 +70,7 @@ public NodesInfoRequestBuilder setJvm(boolean jvm) { * Should the node thread pool info be returned. */ public NodesInfoRequestBuilder setThreadPool(boolean threadPool) { - addOrRemoveMetric(threadPool, NodesInfoRequest.Metric.THREAD_POOL); + addOrRemoveMetric(threadPool, NodesInfoMetrics.Metric.THREAD_POOL); return this; } @@ -78,7 +78,7 @@ public NodesInfoRequestBuilder setThreadPool(boolean threadPool) { * Should the node Transport info be returned. */ public NodesInfoRequestBuilder setTransport(boolean transport) { - addOrRemoveMetric(transport, NodesInfoRequest.Metric.TRANSPORT); + addOrRemoveMetric(transport, NodesInfoMetrics.Metric.TRANSPORT); return this; } @@ -86,7 +86,7 @@ public NodesInfoRequestBuilder setTransport(boolean transport) { * Should the node HTTP info be returned. */ public NodesInfoRequestBuilder setHttp(boolean http) { - addOrRemoveMetric(http, NodesInfoRequest.Metric.HTTP); + addOrRemoveMetric(http, NodesInfoMetrics.Metric.HTTP); return this; } @@ -94,7 +94,7 @@ public NodesInfoRequestBuilder setHttp(boolean http) { * Should the node plugins info be returned. */ public NodesInfoRequestBuilder setPlugins(boolean plugins) { - addOrRemoveMetric(plugins, NodesInfoRequest.Metric.PLUGINS); + addOrRemoveMetric(plugins, NodesInfoMetrics.Metric.PLUGINS); return this; } @@ -102,7 +102,7 @@ public NodesInfoRequestBuilder setPlugins(boolean plugins) { * Should the node ingest info be returned. */ public NodesInfoRequestBuilder setIngest(boolean ingest) { - addOrRemoveMetric(ingest, NodesInfoRequest.Metric.INGEST); + addOrRemoveMetric(ingest, NodesInfoMetrics.Metric.INGEST); return this; } @@ -110,11 +110,11 @@ public NodesInfoRequestBuilder setIngest(boolean ingest) { * Should the node indices info be returned. */ public NodesInfoRequestBuilder setIndices(boolean indices) { - addOrRemoveMetric(indices, NodesInfoRequest.Metric.INDICES); + addOrRemoveMetric(indices, NodesInfoMetrics.Metric.INDICES); return this; } - private void addOrRemoveMetric(boolean includeMetric, NodesInfoRequest.Metric metric) { + private void addOrRemoveMetric(boolean includeMetric, NodesInfoMetrics.Metric metric) { if (includeMetric) { request.addMetric(metric.metricName()); } else { diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/TransportNodesInfoAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/TransportNodesInfoAction.java index ccd6ccde2dabd..14dffce86daa5 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/TransportNodesInfoAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/info/TransportNodesInfoAction.java @@ -79,18 +79,18 @@ protected NodeInfo nodeOperation(NodeInfoRequest nodeRequest, Task task) { NodesInfoRequest request = nodeRequest.request; Set metrics = request.requestedMetrics(); return nodeService.info( - metrics.contains(NodesInfoRequest.Metric.SETTINGS.metricName()), - metrics.contains(NodesInfoRequest.Metric.OS.metricName()), - metrics.contains(NodesInfoRequest.Metric.PROCESS.metricName()), - metrics.contains(NodesInfoRequest.Metric.JVM.metricName()), - metrics.contains(NodesInfoRequest.Metric.THREAD_POOL.metricName()), - metrics.contains(NodesInfoRequest.Metric.TRANSPORT.metricName()), - metrics.contains(NodesInfoRequest.Metric.HTTP.metricName()), - metrics.contains(NodesInfoRequest.Metric.REMOTE_CLUSTER_SERVER.metricName()), - metrics.contains(NodesInfoRequest.Metric.PLUGINS.metricName()), - metrics.contains(NodesInfoRequest.Metric.INGEST.metricName()), - metrics.contains(NodesInfoRequest.Metric.AGGREGATIONS.metricName()), - metrics.contains(NodesInfoRequest.Metric.INDICES.metricName()) + metrics.contains(NodesInfoMetrics.Metric.SETTINGS.metricName()), + metrics.contains(NodesInfoMetrics.Metric.OS.metricName()), + metrics.contains(NodesInfoMetrics.Metric.PROCESS.metricName()), + metrics.contains(NodesInfoMetrics.Metric.JVM.metricName()), + metrics.contains(NodesInfoMetrics.Metric.THREAD_POOL.metricName()), + metrics.contains(NodesInfoMetrics.Metric.TRANSPORT.metricName()), + metrics.contains(NodesInfoMetrics.Metric.HTTP.metricName()), + metrics.contains(NodesInfoMetrics.Metric.REMOTE_CLUSTER_SERVER.metricName()), + metrics.contains(NodesInfoMetrics.Metric.PLUGINS.metricName()), + metrics.contains(NodesInfoMetrics.Metric.INGEST.metricName()), + metrics.contains(NodesInfoMetrics.Metric.AGGREGATIONS.metricName()), + metrics.contains(NodesInfoMetrics.Metric.INDICES.metricName()) ); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java index 5898f258865d7..49d1dde59477b 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.support.ActionFilters; @@ -108,7 +109,7 @@ protected void doExecute(Task task, Request request, ActionListener li threadContext.markAsSystemContext(); if (request.remoteClusterServer) { final NodesInfoRequest nodesInfoRequest = new NodesInfoRequest().clear() - .addMetrics(NodesInfoRequest.Metric.REMOTE_CLUSTER_SERVER.metricName()); + .addMetrics(NodesInfoMetrics.Metric.REMOTE_CLUSTER_SERVER.metricName()); transportService.sendRequest( transportService.getLocalNode(), NodesInfoAction.NAME, diff --git a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java index 65d5ad5807ecb..b1e2533d1038d 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineTransportAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.action.ingest; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.AcknowledgedResponse; @@ -66,7 +67,7 @@ protected void masterOperation(Task task, PutPipelineRequest request, ClusterSta ingestService.putPipeline(request, listener, (nodeListener) -> { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); nodesInfoRequest.clear(); - nodesInfoRequest.addMetric(NodesInfoRequest.Metric.INGEST.metricName()); + nodesInfoRequest.addMetric(NodesInfoMetrics.Metric.INGEST.metricName()); client.admin().cluster().nodesInfo(nodesInfoRequest, nodeListener); }); } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesInfoAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesInfoAction.java index ad591c07ffec0..fc04374411536 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesInfoAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesInfoAction.java @@ -8,6 +8,7 @@ package org.elasticsearch.rest.action.admin.cluster; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.common.Strings; @@ -28,7 +29,7 @@ @ServerlessScope(Scope.INTERNAL) public class RestNodesInfoAction extends BaseRestHandler { - static final Set ALLOWED_METRICS = NodesInfoRequest.Metric.allMetrics(); + static final Set ALLOWED_METRICS = NodesInfoMetrics.Metric.allMetrics(); private final SettingsFilter settingsFilter; diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java index a9a1a09a4f01c..9a032ce064cf6 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.rest.action.cat; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; @@ -60,7 +61,7 @@ public RestChannelConsumer doCatRequest(final RestRequest request, final NodeCli @Override public void processResponse(final ClusterStateResponse clusterStateResponse) { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); - nodesInfoRequest.clear().addMetric(NodesInfoRequest.Metric.PROCESS.metricName()); + nodesInfoRequest.clear().addMetric(NodesInfoMetrics.Metric.PROCESS.metricName()); client.admin().cluster().nodesInfo(nodesInfoRequest, new RestResponseListener(channel) { @Override public RestResponse buildResponse(NodesInfoResponse nodesInfoResponse) throws Exception { diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java index 296daeeedb7d1..1c4d5f7d8fdbd 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.rest.action.cat; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; @@ -90,10 +91,10 @@ public void processResponse(final ClusterStateResponse clusterStateResponse) { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); nodesInfoRequest.clear() .addMetrics( - NodesInfoRequest.Metric.JVM.metricName(), - NodesInfoRequest.Metric.OS.metricName(), - NodesInfoRequest.Metric.PROCESS.metricName(), - NodesInfoRequest.Metric.HTTP.metricName() + NodesInfoMetrics.Metric.JVM.metricName(), + NodesInfoMetrics.Metric.OS.metricName(), + NodesInfoMetrics.Metric.PROCESS.metricName(), + NodesInfoMetrics.Metric.HTTP.metricName() ); client.admin().cluster().nodesInfo(nodesInfoRequest, new RestActionListener(channel) { @Override diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestPluginsAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestPluginsAction.java index 16ad6b4c289e4..7aba2c8e38a6d 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestPluginsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestPluginsAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.rest.action.cat; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.PluginsAndModules; @@ -61,7 +62,7 @@ public RestChannelConsumer doCatRequest(final RestRequest request, final NodeCli @Override public void processResponse(final ClusterStateResponse clusterStateResponse) throws Exception { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); - nodesInfoRequest.clear().addMetric(NodesInfoRequest.Metric.PLUGINS.metricName()); + nodesInfoRequest.clear().addMetric(NodesInfoMetrics.Metric.PLUGINS.metricName()); client.admin().cluster().nodesInfo(nodesInfoRequest, new RestResponseListener(channel) { @Override public RestResponse buildResponse(final NodesInfoResponse nodesInfoResponse) throws Exception { diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java index 727bd361e5def..c94f40b83856e 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestThreadPoolAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.rest.action.cat; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; @@ -77,7 +78,7 @@ public RestChannelConsumer doCatRequest(final RestRequest request, final NodeCli public void processResponse(final ClusterStateResponse clusterStateResponse) { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); nodesInfoRequest.clear() - .addMetrics(NodesInfoRequest.Metric.PROCESS.metricName(), NodesInfoRequest.Metric.THREAD_POOL.metricName()); + .addMetrics(NodesInfoMetrics.Metric.PROCESS.metricName(), NodesInfoMetrics.Metric.THREAD_POOL.metricName()); client.admin().cluster().nodesInfo(nodesInfoRequest, new RestActionListener(channel) { @Override public void processResponse(final NodesInfoResponse nodesInfoResponse) { diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestTests.java index a572625fb9f9c..16c0ed251aa3d 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/info/NodesInfoRequestTests.java @@ -32,7 +32,7 @@ public class NodesInfoRequestTests extends ESTestCase { */ public void testAddMetricsSet() throws Exception { NodesInfoRequest request = new NodesInfoRequest(randomAlphaOfLength(8)); - randomSubsetOf(NodesInfoRequest.Metric.allMetrics()).forEach(request::addMetric); + randomSubsetOf(NodesInfoMetrics.Metric.allMetrics()).forEach(request::addMetric); NodesInfoRequest deserializedRequest = roundTripRequest(request); assertThat(request.requestedMetrics(), equalTo(deserializedRequest.requestedMetrics())); } @@ -42,7 +42,7 @@ public void testAddMetricsSet() throws Exception { */ public void testAddSingleMetric() throws Exception { NodesInfoRequest request = new NodesInfoRequest(randomAlphaOfLength(8)); - request.addMetric(randomFrom(NodesInfoRequest.Metric.allMetrics())); + request.addMetric(randomFrom(NodesInfoMetrics.Metric.allMetrics())); NodesInfoRequest deserializedRequest = roundTripRequest(request); assertThat(request.requestedMetrics(), equalTo(deserializedRequest.requestedMetrics())); } @@ -53,7 +53,7 @@ public void testAddSingleMetric() throws Exception { public void testRemoveSingleMetric() throws Exception { NodesInfoRequest request = new NodesInfoRequest(randomAlphaOfLength(8)); request.all(); - String metric = randomFrom(NodesInfoRequest.Metric.allMetrics()); + String metric = randomFrom(NodesInfoMetrics.Metric.allMetrics()); request.removeMetric(metric); NodesInfoRequest deserializedRequest = roundTripRequest(request); @@ -63,7 +63,7 @@ public void testRemoveSingleMetric() throws Exception { /** * Test that a newly constructed NodesInfoRequestObject requests all of the - * possible metrics defined in {@link NodesInfoRequest.Metric}. + * possible metrics defined in {@link NodesInfoMetrics.Metric}. */ public void testNodesInfoRequestDefaults() { NodesInfoRequest defaultNodesInfoRequest = new NodesInfoRequest(randomAlphaOfLength(8)); @@ -80,7 +80,7 @@ public void testNodesInfoRequestAll() throws Exception { NodesInfoRequest request = new NodesInfoRequest("node"); request.all(); - assertThat(request.requestedMetrics(), equalTo(NodesInfoRequest.Metric.allMetrics())); + assertThat(request.requestedMetrics(), equalTo(NodesInfoMetrics.Metric.allMetrics())); } /** @@ -101,7 +101,7 @@ public void testUnknownMetricsRejected() { String unknownMetric2 = "unknown_metric2"; Set unknownMetrics = new HashSet<>(); unknownMetrics.add(unknownMetric1); - unknownMetrics.addAll(randomSubsetOf(NodesInfoRequest.Metric.allMetrics())); + unknownMetrics.addAll(randomSubsetOf(NodesInfoMetrics.Metric.allMetrics())); NodesInfoRequest request = new NodesInfoRequest(); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java index 7c6c1ffdcf98b..18c586a4a51ae 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.action.ActionListenerResponseHandler; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.support.ActionFilters; @@ -108,7 +109,7 @@ public void testDoExecuteForRemoteServerNodes() { doAnswer(invocation -> { final NodesInfoRequest nodesInfoRequest = invocation.getArgument(2); - assertThat(nodesInfoRequest.requestedMetrics(), containsInAnyOrder(NodesInfoRequest.Metric.REMOTE_CLUSTER_SERVER.metricName())); + assertThat(nodesInfoRequest.requestedMetrics(), containsInAnyOrder(NodesInfoMetrics.Metric.REMOTE_CLUSTER_SERVER.metricName())); final ActionListenerResponseHandler handler = invocation.getArgument(3); handler.handleResponse(nodesInfoResponse); return null; diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodeInfoService.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodeInfoService.java index 118cd19d01b79..578689828de6b 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodeInfoService.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/capacity/nodeinfo/AutoscalingNodeInfoService.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.FailedNodeException; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest; import org.elasticsearch.action.support.nodes.BaseNodeResponse; @@ -138,7 +139,7 @@ private void sendToMissingNodes(Function nodeLookup, Set< .map(BaseNodeResponse::getNode) .map(DiscoveryNode::getId) .toArray(String[]::new) - ).clear().addMetric(NodesInfoRequest.Metric.OS.metricName()).timeout(fetchTimeout), + ).clear().addMetric(NodesInfoMetrics.Metric.OS.metricName()).timeout(fetchTimeout), ActionListener.wrap(nodesInfoResponse -> { final Map builderBuilder = Maps.newHashMapWithExpectedSize( nodesStatsResponse.getNodes().size() diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentAction.java index 65747c94590b5..152c6aeaf14e7 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/enrollment/TransportNodeEnrollmentAction.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; @@ -142,7 +143,7 @@ protected void doExecute(Task task, NodeEnrollmentRequest request, ActionListene } final List nodeList = new ArrayList<>(); - final NodesInfoRequest nodesInfoRequest = new NodesInfoRequest().addMetric(NodesInfoRequest.Metric.TRANSPORT.metricName()); + final NodesInfoRequest nodesInfoRequest = new NodesInfoRequest().addMetric(NodesInfoMetrics.Metric.TRANSPORT.metricName()); executeAsyncWithOrigin(client, SECURITY_ORIGIN, NodesInfoAction.INSTANCE, nodesInfoRequest, ActionListener.wrap(response -> { for (NodeInfo nodeInfo : response.getNodes()) { nodeList.add(nodeInfo.getInfo(TransportInfo.class).getAddress().publishAddress().toString()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGenerator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGenerator.java index 1e874dead9956..d0acc61570853 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGenerator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/InternalEnrollmentTokenGenerator.java @@ -13,6 +13,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoMetrics; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.internal.Client; @@ -65,7 +66,7 @@ public InternalEnrollmentTokenGenerator(Environment environment, SSLService sslS public void maybeCreateNodeEnrollmentToken(Consumer consumer, Iterator backoff) { // the enrollment token can only be used against the node that generated it final NodesInfoRequest nodesInfoRequest = new NodesInfoRequest().nodesIds("_local") - .addMetrics(NodesInfoRequest.Metric.HTTP.metricName(), NodesInfoRequest.Metric.TRANSPORT.metricName()); + .addMetrics(NodesInfoMetrics.Metric.HTTP.metricName(), NodesInfoMetrics.Metric.TRANSPORT.metricName()); client.execute(NodesInfoAction.INSTANCE, nodesInfoRequest, ActionListener.wrap(response -> { assert response.getNodes().size() == 1; @@ -133,7 +134,7 @@ public void maybeCreateNodeEnrollmentToken(Consumer consumer, Iterator consumer, Iterator backoff) { // the enrollment token can only be used against the node that generated it final NodesInfoRequest nodesInfoRequest = new NodesInfoRequest().nodesIds("_local") - .addMetric(NodesInfoRequest.Metric.HTTP.metricName()); + .addMetric(NodesInfoMetrics.Metric.HTTP.metricName()); client.execute(NodesInfoAction.INSTANCE, nodesInfoRequest, ActionListener.wrap(response -> { assert response.getNodes().size() == 1; NodeInfo nodeInfo = response.getNodes().get(0); From 075e7c68bd3f80b20eac28383234462791e972e8 Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 28 Sep 2023 13:23:53 +0300 Subject: [PATCH 121/155] Muting failing test MixedClusterClientYamlTestSuiteIT test {p0=search.vectors/90_sparse_vector/Sparse vector in 7.x} (#100009) --- .../rest-api-spec/test/search.vectors/90_sparse_vector.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml index 3644e93d12bbd..27aa0e6e9a20b 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/90_sparse_vector.yml @@ -145,8 +145,8 @@ "Sparse vector in 7.x": - skip: features: allowed_warnings - version: "8.0.0 - " - reason: "sparse_vector field type supported in 7.x" + version: "all" + reason: "AwaitsFix https://github.com/elastic/elasticsearch/issues/100003" - do: allowed_warnings: - "The [sparse_vector] field type is deprecated and will be removed in 8.0." From a0b0e25e091d79537634300e7ac94d3a2be8f6ac Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Thu, 28 Sep 2023 12:55:45 +0200 Subject: [PATCH 122/155] Simplify FieldCaps Transport Messages Code (#100010) This removes the intermediate GroupBy record and a few list allocations to reduce the memory and runtime overhead of large responses when there isn't as much deduplication. This should help visibly with the not-as-much-deduplication case already, but more importantly it sets up a massive improvement in a follow-up that I'd open right after this is merged! We can assume that the `IndexFieldCapabilities` instances (now a record to make this obvious) will be duplicated across entries massively. So we can deduplicate them when writing through a lookup table, massively shrinking the size of these responses and making operations on the coordinating node much faster as well. --- .../FieldCapabilitiesIndexResponse.java | 75 +++++----- .../fieldcaps/IndexFieldCapabilities.java | 138 +++++------------- .../action/fieldcaps/ResponseRewriter.java | 10 +- .../TransportFieldCapabilitiesAction.java | 7 +- .../FieldCapabilitiesNodeResponseTests.java | 4 +- .../FieldCapabilitiesResponseTests.java | 4 +- 6 files changed, 83 insertions(+), 155 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java index aac322c4a1de7..b1f844ed66215 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java @@ -16,12 +16,11 @@ import org.elasticsearch.core.Nullable; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; final class FieldCapabilitiesIndexResponse implements Writeable { private static final TransportVersion MAPPING_HASH_VERSION = TransportVersions.V_8_2_0; @@ -48,7 +47,7 @@ final class FieldCapabilitiesIndexResponse implements Writeable { FieldCapabilitiesIndexResponse(StreamInput in) throws IOException { this.indexName = in.readString(); - this.responseMap = in.readMap(IndexFieldCapabilities::new); + this.responseMap = in.readMap(IndexFieldCapabilities::readFrom); this.canMatch = in.readBoolean(); this.originVersion = in.getTransportVersion(); if (in.getTransportVersion().onOrAfter(MAPPING_HASH_VERSION)) { @@ -68,32 +67,25 @@ public void writeTo(StreamOutput out) throws IOException { } } - private record GroupByMappingHash(List indices, String indexMappingHash, Map responseMap) - implements - Writeable { - GroupByMappingHash(StreamInput in) throws IOException { - this(in.readStringCollectionAsList(), in.readString(), in.readMap(IndexFieldCapabilities::new)); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeStringCollection(indices); - out.writeString(indexMappingHash); - out.writeMap(responseMap, StreamOutput::writeWriteable); - } - - Stream getResponses() { - return indices.stream().map(index -> new FieldCapabilitiesIndexResponse(index, indexMappingHash, responseMap, true)); - } - } - static List readList(StreamInput input) throws IOException { if (input.getTransportVersion().before(MAPPING_HASH_VERSION)) { return input.readCollectionAsList(FieldCapabilitiesIndexResponse::new); } - final List ungroupedList = input.readCollectionAsList(FieldCapabilitiesIndexResponse::new); - final List groups = input.readCollectionAsList(GroupByMappingHash::new); - return Stream.concat(ungroupedList.stream(), groups.stream().flatMap(GroupByMappingHash::getResponses)).toList(); + final int ungrouped = input.readVInt(); + final ArrayList responses = new ArrayList<>(ungrouped); + for (int i = 0; i < ungrouped; i++) { + responses.add(new FieldCapabilitiesIndexResponse(input)); + } + final int groups = input.readVInt(); + for (int i = 0; i < groups; i++) { + final List indices = input.readStringCollectionAsList(); + final String mappingHash = input.readString(); + final Map ifc = input.readMap(IndexFieldCapabilities::readFrom); + for (String index : indices) { + responses.add(new FieldCapabilitiesIndexResponse(index, mappingHash, ifc, true)); + } + } + return responses; } static void writeList(StreamOutput output, List responses) throws IOException { @@ -101,22 +93,23 @@ static void writeList(StreamOutput output, List output.writeCollection(responses); return; } - final Predicate canGroup = r -> r.canMatch && r.indexMappingHash != null; - final List ungroupedResponses = responses.stream().filter(r -> canGroup.test(r) == false).toList(); - final List groupedResponses = responses.stream() - .filter(canGroup) - .collect(Collectors.groupingBy(r -> r.indexMappingHash)) - .values() - .stream() - .map(rs -> { - final String indexMappingHash = rs.get(0).indexMappingHash; - final Map responseMap = rs.get(0).responseMap; - final List indices = rs.stream().map(r -> r.indexName).toList(); - return new GroupByMappingHash(indices, indexMappingHash, responseMap); - }) - .toList(); + + Map> groupedResponsesMap = new HashMap<>(); + final List ungroupedResponses = new ArrayList<>(); + for (FieldCapabilitiesIndexResponse r : responses) { + if (r.canMatch && r.indexMappingHash != null) { + groupedResponsesMap.computeIfAbsent(r.indexMappingHash, k -> new ArrayList<>()).add(r); + } else { + ungroupedResponses.add(r); + } + } output.writeCollection(ungroupedResponses); - output.writeCollection(groupedResponses); + output.writeCollection(groupedResponsesMap.values(), (o, fieldCapabilitiesIndexResponses) -> { + o.writeCollection(fieldCapabilitiesIndexResponses, (oo, r) -> oo.writeString(r.indexName)); + var first = fieldCapabilitiesIndexResponses.get(0); + o.writeString(first.indexMappingHash); + o.writeMap(first.responseMap, StreamOutput::writeWriteable); + }); } /** diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java index 57a9dd049d26c..ef609a06cb8be 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java @@ -17,65 +17,54 @@ import java.io.IOException; import java.util.Map; -import java.util.Objects; /** * Describes the capabilities of a field in a single index. + * @param name The name of the field. + * @param type The type associated with the field. + * @param isSearchable Whether this field is indexed for search. + * @param isAggregatable Whether this field can be aggregated on. + * @param meta Metadata about the field. */ -public class IndexFieldCapabilities implements Writeable { - private static final StringLiteralDeduplicator typeStringDeduplicator = new StringLiteralDeduplicator(); - - private final String name; - private final String type; - private final boolean isMetadatafield; - private final boolean isSearchable; - private final boolean isAggregatable; - private final boolean isDimension; - private final TimeSeriesParams.MetricType metricType; - private final Map meta; +public record IndexFieldCapabilities( + String name, + String type, + boolean isMetadatafield, + boolean isSearchable, + boolean isAggregatable, + boolean isDimension, + TimeSeriesParams.MetricType metricType, + Map meta +) implements Writeable { - /** - * @param name The name of the field. - * @param type The type associated with the field. - * @param isSearchable Whether this field is indexed for search. - * @param isAggregatable Whether this field can be aggregated on. - * @param meta Metadata about the field. - */ - IndexFieldCapabilities( - String name, - String type, - boolean isMetadatafield, - boolean isSearchable, - boolean isAggregatable, - boolean isDimension, - TimeSeriesParams.MetricType metricType, - Map meta - ) { - this.name = name; - this.type = type; - this.isMetadatafield = isMetadatafield; - this.isSearchable = isSearchable; - this.isAggregatable = isAggregatable; - this.isDimension = isDimension; - this.metricType = metricType; - this.meta = meta; - } + private static final StringLiteralDeduplicator typeStringDeduplicator = new StringLiteralDeduplicator(); - IndexFieldCapabilities(StreamInput in) throws IOException { - this.name = in.readString(); - this.type = typeStringDeduplicator.deduplicate(in.readString()); - this.isMetadatafield = in.readBoolean(); - this.isSearchable = in.readBoolean(); - this.isAggregatable = in.readBoolean(); + public static IndexFieldCapabilities readFrom(StreamInput in) throws IOException { + String name = in.readString(); + String type = typeStringDeduplicator.deduplicate(in.readString()); + boolean isMetadatafield = in.readBoolean(); + boolean isSearchable = in.readBoolean(); + boolean isAggregatable = in.readBoolean(); + boolean isDimension; + TimeSeriesParams.MetricType metricType; if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_0_0)) { - this.isDimension = in.readBoolean(); - this.metricType = in.readOptionalEnum(TimeSeriesParams.MetricType.class); + isDimension = in.readBoolean(); + metricType = in.readOptionalEnum(TimeSeriesParams.MetricType.class); } else { - this.isDimension = false; - this.metricType = null; + isDimension = false; + metricType = null; } - this.meta = in.readMap(StreamInput::readString); + return new IndexFieldCapabilities( + name, + type, + isMetadatafield, + isSearchable, + isAggregatable, + isDimension, + metricType, + in.readMap(StreamInput::readString) + ); } @Override @@ -92,55 +81,4 @@ public void writeTo(StreamOutput out) throws IOException { out.writeMap(meta, StreamOutput::writeString); } - public String getName() { - return name; - } - - public String getType() { - return type; - } - - public boolean isMetadatafield() { - return isMetadatafield; - } - - public boolean isAggregatable() { - return isAggregatable; - } - - public boolean isSearchable() { - return isSearchable; - } - - public boolean isDimension() { - return isDimension; - } - - public TimeSeriesParams.MetricType getMetricType() { - return metricType; - } - - public Map meta() { - return meta; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - IndexFieldCapabilities that = (IndexFieldCapabilities) o; - return isMetadatafield == that.isMetadatafield - && isSearchable == that.isSearchable - && isAggregatable == that.isAggregatable - && isDimension == that.isDimension - && Objects.equals(metricType, that.metricType) - && Objects.equals(name, that.name) - && Objects.equals(type, that.type) - && Objects.equals(meta, that.meta); - } - - @Override - public int hashCode() { - return Objects.hash(name, type, isMetadatafield, isSearchable, isAggregatable, isDimension, metricType, meta); - } } diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/ResponseRewriter.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/ResponseRewriter.java index 37313c435319c..d39dd28b32611 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/ResponseRewriter.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/ResponseRewriter.java @@ -54,11 +54,11 @@ private static Function buildTra Set nestedObjects = null; if (allowedTypes.length > 0) { Set at = Set.of(allowedTypes); - test = test.and(ifc -> at.contains(ifc.getType())); + test = test.and(ifc -> at.contains(ifc.type())); } for (String filter : filters) { if ("-parent".equals(filter)) { - test = test.and(fc -> fc.getType().equals("nested") == false && fc.getType().equals("object") == false); + test = test.and(fc -> fc.type().equals("nested") == false && fc.type().equals("object") == false); } if ("-metadata".equals(filter)) { test = test.and(fc -> fc.isMetadatafield() == false); @@ -71,7 +71,7 @@ private static Function buildTra nestedObjects = findTypes("nested", input); } Set no = nestedObjects; - test = test.and(fc -> isNestedField(fc.getName(), no) == false); + test = test.and(fc -> isNestedField(fc.name(), no) == false); } if ("-multifield".equals(filter)) { // immediate parent is not an object field @@ -79,7 +79,7 @@ private static Function buildTra objects = findTypes("object", input); } Set o = objects; - test = test.and(fc -> isNotMultifield(fc.getName(), o)); + test = test.and(fc -> isNotMultifield(fc.name(), o)); } } Predicate finalTest = test; @@ -94,7 +94,7 @@ private static Function buildTra private static Set findTypes(String type, Map fieldCaps) { return fieldCaps.entrySet() .stream() - .filter(entry -> type.equals(entry.getValue().getType())) + .filter(entry -> type.equals(entry.getValue().type())) .map(Map.Entry::getKey) .collect(Collectors.toSet()); } diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index 91c4f10956866..c9a44c14106ee 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -417,17 +417,14 @@ private static void innerMerge( final String field = entry.getKey(); final IndexFieldCapabilities fieldCap = entry.getValue(); Map typeMap = responseMapBuilder.computeIfAbsent(field, f -> new HashMap<>()); - FieldCapabilities.Builder builder = typeMap.computeIfAbsent( - fieldCap.getType(), - key -> new FieldCapabilities.Builder(field, key) - ); + FieldCapabilities.Builder builder = typeMap.computeIfAbsent(fieldCap.type(), key -> new FieldCapabilities.Builder(field, key)); builder.add( indices, fieldCap.isMetadatafield(), fieldCap.isSearchable(), fieldCap.isAggregatable(), fieldCap.isDimension(), - fieldCap.getMetricType(), + fieldCap.metricType(), fieldCap.meta() ); } 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 a95340e2fffd1..0802e498c43a7 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesNodeResponseTests.java @@ -161,8 +161,8 @@ public void testSerializeNodeResponseBetweenOldNodes() throws IOException { // Exclude metric types which was introduced in 8.0 assertThat(outCap.keySet(), equalTo(inCap.keySet())); for (String field : outCap.keySet()) { - assertThat(outCap.get(field).getName(), equalTo(inCap.get(field).getName())); - assertThat(outCap.get(field).getType(), equalTo(inCap.get(field).getType())); + 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/fieldcaps/FieldCapabilitiesResponseTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java index cc7c76553ef99..461000fc22b02 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java @@ -235,8 +235,8 @@ public void testSerializeCCSResponseBetweenOldClusters() throws IOException { // Exclude metric types which was introduced in 8.0 assertThat(outCap.keySet(), equalTo(inCap.keySet())); for (String field : outCap.keySet()) { - assertThat(outCap.get(field).getName(), equalTo(inCap.get(field).getName())); - assertThat(outCap.get(field).getType(), equalTo(inCap.get(field).getType())); + 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())); From 84350c65f553f0f4cc9bd852bbe4d272dea111b4 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 28 Sep 2023 13:34:17 +0200 Subject: [PATCH 123/155] Adjust executor queue size in AggregatorTestCase (#100011) The queue size needs to be unbounded like the search workers thread pool, to prevent rejections. Rather than setting the queue size to a negative number, we can reuse the search workers executor that is already part of the created thread pool instance. --- .../search/aggregations/AggregatorTestCase.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index d19fd4ecf08a3..5a28bd8b0ea6d 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -59,8 +59,6 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; -import org.elasticsearch.common.util.concurrent.EsExecutors; -import org.elasticsearch.common.util.concurrent.EsExecutors.TaskTrackingConfig; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; @@ -201,16 +199,8 @@ public abstract class AggregatorTestCase extends ESTestCase { @Before public final void initPlugins() { - int numThreads = randomIntBetween(2, 4); threadPool = new TestThreadPool(AggregatorTestCase.class.getName()); - threadPoolExecutor = EsExecutors.newFixed( - "test", - numThreads, - 10, - EsExecutors.daemonThreadFactory("test"), - threadPool.getThreadContext(), - randomFrom(TaskTrackingConfig.DEFAULT, TaskTrackingConfig.DO_NOT_TRACK) - ); + threadPoolExecutor = (ThreadPoolExecutor) threadPool.executor(ThreadPool.Names.SEARCH_WORKER); List plugins = new ArrayList<>(getSearchPlugins()); plugins.add(new AggCardinalityUpperBoundPlugin()); SearchModule searchModule = new SearchModule(Settings.EMPTY, plugins); From d6526f8d4b48de145c893dc39bcb9ccf02a5ae04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:41:53 +0200 Subject: [PATCH 124/155] [Index Management] Update docs screenshots with for the new index details page (#99973) --- .../index-mgmt/management_index_details.png | Bin 339652 -> 422199 bytes docs/reference/indices/index-mgmt.asciidoc | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/images/index-mgmt/management_index_details.png b/docs/reference/images/index-mgmt/management_index_details.png index edf1a6517f6a2dc7ad7746456f22ea313a85ae77..a975b9952ca88762f7db2d91929a9f309aacd22c 100644 GIT binary patch literal 422199 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 =vRYq4Af5`0?4BUcG2H!Hu`@OeS6m@}LECNYEj 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
    jPsu-7C2W<jHqHMjON}N*HPx-=g%Wf0{qnA@u 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 O1L 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 iz{~v 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 diff --git a/docs/reference/indices/index-mgmt.asciidoc b/docs/reference/indices/index-mgmt.asciidoc index 571f1c813fbfa..b89d2ec957af9 100644 --- a/docs/reference/indices/index-mgmt.asciidoc +++ b/docs/reference/indices/index-mgmt.asciidoc @@ -49,11 +49,11 @@ Badges indicate if an index is a <>, a Clicking a badge narrows the list to only indices of that type. You can also filter indices using the search bar. -You can drill down into each index to investigate the index +By clicking the index name, you can open an index details page to investigate the index <>, <>, and statistics. -From this view, you can also edit the index settings. +On this page, you can also edit the index settings. -To view and explore the documents within an index, click the compass icon image:compassicon.png[width=3%] next to the index name to open {kibana-ref}/discover.html[Discover]. +To view and explore the documents within an index, click the *Discover index* button to open {kibana-ref}/discover.html[Discover]. [role="screenshot"] image::images/index-mgmt/management_index_details.png[Index Management UI] From 787bca43deea01170f469ef4615ad697130aff27 Mon Sep 17 00:00:00 2001 From: Matteo Piergiovanni <134913285+piergm@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:50:59 +0200 Subject: [PATCH 125/155] Mute SearchResponseTests#testSerialization (#100005) (#100015) --- .../org/elasticsearch/action/search/SearchResponseTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index befea803e6fa0..9acb63db2cc90 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -590,6 +590,7 @@ public void testToXContent() throws IOException { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100005") public void testSerialization() throws IOException { SearchResponse searchResponse = createTestItem(false); SearchResponse deserialized = copyWriteable( From 2499f6c7b0a4d7d20fd223bfb220951779c881d2 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 28 Sep 2023 13:53:21 +0100 Subject: [PATCH 126/155] [ML] Use TransportVersion for remote cluster age check (#100013) Previously the code was using MlConfigVersion for a remote cluster version check, which is inappropriate as the communications with the remote cluster are transport, not config. --- .../common/validation/SourceDestValidator.java | 3 --- .../xpack/core/ml/MlConfigVersion.java | 3 ++- .../xpack/core/ml/datafeed/DatafeedConfig.java | 7 ++++--- .../ml/action/TransportStartDatafeedAction.java | 16 ++++++++-------- .../TransportStartDatafeedActionTests.java | 13 +++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java index 49bdf60ad0b18..9962f14ec3736 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java @@ -73,9 +73,6 @@ public final class SourceDestValidator { public static final String REMOTE_CLUSTERS_TRANSPORT_TOO_OLD = "remote clusters are expected to run at least transport version [{0}] (reason: [{1}])," + " but the following clusters were too old: [{2}]"; - public static final String REMOTE_CLUSTERS_CONFIG_TOO_OLD = - "remote clusters are expected to run at least config version [{0}] (reason: [{1}])," - + " but the following clusters were too old: [{2}]"; public static final String PIPELINE_MISSING = "Pipeline with id [{0}] could not be found"; private final IndexNameExpressionResolver indexNameExpressionResolver; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java index 75370db1d766f..e631e3efe5cb6 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java @@ -268,7 +268,8 @@ public static MlConfigVersion max(MlConfigVersion version1, MlConfigVersion vers return version1.id > version2.id ? version1 : version2; } - public static MlConfigVersion fromVersion(Version version) { + // Visible only for testing + static MlConfigVersion fromVersion(Version version) { if (version.equals(Version.V_8_10_0)) { return V_10; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java index 7f5de886222ba..cacc4a6a33196 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java @@ -9,6 +9,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.SimpleDiffable; @@ -36,7 +38,6 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.common.time.TimeUtils; -import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.datafeed.extractor.ExtractorUtils; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.core.ml.utils.MlStrings; @@ -86,7 +87,7 @@ */ public class DatafeedConfig implements SimpleDiffable, ToXContentObject { - private static final MlConfigVersion RUNTIME_MAPPINGS_INTRODUCED = MlConfigVersion.V_7_11_0; + private static final TransportVersion RUNTIME_MAPPINGS_INTRODUCED = TransportVersions.V_7_11_0; public static final int DEFAULT_SCROLL_SIZE = 1000; @@ -340,7 +341,7 @@ public Integer getScrollSize() { return scrollSize; } - public Optional> minRequiredConfigVersion() { + public Optional> minRequiredTransportVersion() { return runtimeMappings.isEmpty() ? Optional.empty() : Optional.of(Tuple.tuple(RUNTIME_MAPPINGS_INTRODUCED, SearchSourceBuilder.RUNTIME_MAPPINGS_FIELD.getPreferredName())); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java index 38b827b120bf6..475ca4ef2a7ce 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ResourceAlreadyExistsException; +import org.elasticsearch.TransportVersion; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.TransportMasterNodeAction; @@ -45,7 +46,6 @@ import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.ml.MachineLearningField; -import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.MlTasks; import org.elasticsearch.xpack.core.ml.action.GetDatafeedRunningStateAction; import org.elasticsearch.xpack.core.ml.action.NodeAcknowledgedResponse; @@ -80,7 +80,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.REMOTE_CLUSTERS_CONFIG_TOO_OLD; +import static org.elasticsearch.xpack.core.common.validation.SourceDestValidator.REMOTE_CLUSTERS_TRANSPORT_TOO_OLD; /* This class extends from TransportMasterNodeAction for cluster state observing purposes. The stop datafeed api also redirect the elected master node. @@ -249,7 +249,7 @@ public void onFailure(Exception e) { checkRemoteConfigVersions( datafeedConfigHolder.get(), remoteAliases, - (cn) -> MlConfigVersion.fromVersion(remoteClusterService.getConnection(cn).getVersion()) + (cn) -> remoteClusterService.getConnection(cn).getTransportVersion() ); createDataExtractor(task, job, datafeedConfigHolder.get(), params, waitForTaskListener); } @@ -299,17 +299,17 @@ public void onFailure(Exception e) { static void checkRemoteConfigVersions( DatafeedConfig config, List remoteClusters, - Function configVersionSupplier + Function transportVersionSupplier ) { - Optional> minVersionAndReason = config.minRequiredConfigVersion(); + Optional> minVersionAndReason = config.minRequiredTransportVersion(); if (minVersionAndReason.isPresent() == false) { return; } final String reason = minVersionAndReason.get().v2(); - final MlConfigVersion minVersion = minVersionAndReason.get().v1(); + final TransportVersion minVersion = minVersionAndReason.get().v1(); List clustersTooOld = remoteClusters.stream() - .filter(cn -> configVersionSupplier.apply(cn).before(minVersion)) + .filter(cn -> transportVersionSupplier.apply(cn).before(minVersion)) .collect(Collectors.toList()); if (clustersTooOld.isEmpty()) { return; @@ -317,7 +317,7 @@ static void checkRemoteConfigVersions( throw ExceptionsHelper.badRequestException( Messages.getMessage( - REMOTE_CLUSTERS_CONFIG_TOO_OLD, + REMOTE_CLUSTERS_TRANSPORT_TOO_OLD, minVersion.toString(), reason, Strings.collectionToCommaDelimitedString(clustersTooOld) diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java index 4a66a2836273e..a2d5003118536 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedActionTests.java @@ -8,13 +8,14 @@ package org.elasticsearch.xpack.ml.action; import org.elasticsearch.ElasticsearchStatusException; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.search.SearchModule; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.NamedXContentRegistry; -import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.action.StartDatafeedAction; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfigTests; @@ -112,13 +113,13 @@ public void testNoDeprecationsLogged() { } public void testRemoteClusterVersionCheck() { - Map clusterVersions = Map.of( + Map clusterVersions = Map.of( "modern_cluster_1", - MlConfigVersion.CURRENT, + TransportVersion.current(), "modern_cluster_2", - MlConfigVersion.CURRENT, + TransportVersion.current(), "old_cluster_1", - MlConfigVersion.V_7_0_0 + TransportVersions.V_7_0_0 ); Map field = Map.of("runtime_field_foo", Map.of("type", "keyword", "script", "")); @@ -137,7 +138,7 @@ public void testRemoteClusterVersionCheck() { assertThat( ex.getMessage(), containsString( - "remote clusters are expected to run at least config version [7.11.0] (reason: [runtime_mappings]), " + "remote clusters are expected to run at least transport version [7110099] (reason: [runtime_mappings]), " + "but the following clusters were too old: [old_cluster_1]" ) ); From c6f4cb49143729128c05b104fa383e07b3884061 Mon Sep 17 00:00:00 2001 From: Mary Gouseti Date: Thu, 28 Sep 2023 15:55:39 +0300 Subject: [PATCH 127/155] Inactive replica of new primary shard is green (#99995) When we have a new initialisation of a primary shard we consider the this indicator `green` based on the idea that this are unexceptional events in the cluster's lifecycle. However, when we have a replica of this shard that is inactive we turn to `yellow`. With this PR, we change this behaviour to signal that if the primary is inactive and that is `green`, then an inactive replica of this shard is also `green`. Fixes #99951 --- docs/changelog/99995.yaml | 6 ++++++ ...ShardsAvailabilityHealthIndicatorService.java | 16 +++++++++++++--- ...sAvailabilityHealthIndicatorServiceTests.java | 13 +++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 docs/changelog/99995.yaml diff --git a/docs/changelog/99995.yaml b/docs/changelog/99995.yaml new file mode 100644 index 0000000000000..d67cbdaec1f37 --- /dev/null +++ b/docs/changelog/99995.yaml @@ -0,0 +1,6 @@ +pr: 99995 +summary: When a primary is inactive but this is considered expected, the same applies for the replica of this shard. +area: Health +type: enhancement +issues: + - 99951 diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java index e04e8a47349b6..9d41dd86d2ceb 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java @@ -402,7 +402,7 @@ private class ShardAllocationCounts { private final Map> diagnosisDefinitions = new HashMap<>(); public void increment(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { - boolean isNew = isUnassignedDueToNewInitialization(routing); + boolean isNew = isUnassignedDueToNewInitialization(routing, state); boolean isRestarting = isUnassignedDueToTimelyRestart(routing, shutdowns); available &= routing.active() || isRestarting || isNew; if ((routing.active() || isRestarting || isNew) == false) { @@ -454,8 +454,14 @@ private static boolean isUnassignedDueToTimelyRestart(ShardRouting routing, Node return now - restartingAllocationDelayExpiration <= 0; } - private static boolean isUnassignedDueToNewInitialization(ShardRouting routing) { - return routing.primary() && routing.active() == false && getInactivePrimaryHealth(routing) == ClusterHealthStatus.YELLOW; + private static boolean isUnassignedDueToNewInitialization(ShardRouting routing, ClusterState state) { + if (routing.active()) { + return false; + } + // If the primary is inactive for unexceptional events in the cluster lifecycle, both the primary and the + // replica are considered new initializations. + ShardRouting primary = routing.primary() ? routing : state.routingTable().shardRoutingTable(routing.shardId()).primaryShard(); + return primary.active() == false && getInactivePrimaryHealth(primary) == ClusterHealthStatus.YELLOW; } /** @@ -815,6 +821,7 @@ public String getSymptom() { || primaries.unassigned_new > 0 || primaries.unassigned_restarting > 0 || replicas.unassigned > 0 + || replicas.unassigned_new > 0 || replicas.unassigned_restarting > 0 || primaries.initializing > 0 || replicas.initializing > 0) { @@ -822,6 +829,7 @@ public String getSymptom() { Stream.of( createMessage(primaries.unassigned, "unavailable primary shard", "unavailable primary shards"), createMessage(primaries.unassigned_new, "creating primary shard", "creating primary shards"), + createMessage(replicas.unassigned_new, "creating replica shard", "creating replica shards"), createMessage(primaries.unassigned_restarting, "restarting primary shard", "restarting primary shards"), createMessage(replicas.unassigned, "unavailable replica shard", "unavailable replica shards"), createMessage(primaries.initializing, "initializing primary shard", "initializing primary shards"), @@ -861,6 +869,8 @@ public HealthIndicatorDetails getDetails(boolean verbose) { replicas.unassigned, "initializing_replicas", replicas.initializing, + "creating_replicas", + replicas.unassigned_new, "restarting_replicas", replicas.unassigned_restarting, "started_replicas", diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java index e6da007b085a3..708a3125590fd 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java @@ -533,8 +533,11 @@ public void testShouldBeYellowWhenRestartingReplicasReachedAllocationDelay() { ); } - public void testShouldBeGreenWhenThereAreInitializingPrimaries() { - var clusterState = createClusterStateWith(List.of(index("restarting-index", new ShardAllocation("node-0", CREATING))), List.of()); + public void testShouldBeGreenWhenThereAreInitializingPrimariesAndReplicas() { + var clusterState = createClusterStateWith( + List.of(index("restarting-index", new ShardAllocation("node-0", CREATING), new ShardAllocation("node-1", CREATING))), + List.of() + ); var service = createShardsAvailabilityIndicatorService(clusterState); assertThat( @@ -542,8 +545,8 @@ public void testShouldBeGreenWhenThereAreInitializingPrimaries() { equalTo( createExpectedResult( GREEN, - "This cluster has 1 creating primary shard.", - Map.of("creating_primaries", 1), + "This cluster has 1 creating primary shard, 1 creating replica shard.", + Map.of("creating_primaries", 1, "creating_replicas", 1), emptyList(), emptyList() ) @@ -1765,6 +1768,8 @@ private static Map addDefaults(Map override) { override.getOrDefault("started_primaries", 0), "unassigned_replicas", override.getOrDefault("unassigned_replicas", 0), + "creating_replicas", + override.getOrDefault("creating_replicas", 0), "initializing_replicas", override.getOrDefault("initializing_replicas", 0), "restarting_replicas", From e951d0efd02aa07cf8a1e9495b2fd045f01bd1ce Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:19:00 +0300 Subject: [PATCH 128/155] [TEST] Test that aggregate_metric_double is unsupported in ESQL (#99952) * Test that aggregate_metric_double is unsupported in ESQL * Add from-clause test --- .../resources/rest-api-spec/test/40_tsdb.yml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml index d72d09644a128..2e8c43379d690 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml @@ -60,6 +60,39 @@ setup: - '{"index": {}}' - '{"@timestamp": "2021-04-28T18:51:03.142Z", "metricset": "pod", "k8s": {"pod": {"name": "dog", "uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9", "ip": "10.10.55.3", "network": {"tx": 1434595272, "rx": 530605511}}}}' + - do: + indices.create: + index: test2 + body: + settings: + index: + mode: time_series + routing_path: [ dim ] + time_series: + start_time: 2021-04-28T00:00:00Z + end_time: 2021-04-29T00:00:00Z + mappings: + properties: + "@timestamp": + type: date + dim: + type: keyword + time_series_dimension: true + agg_metric: + type: aggregate_metric_double + metrics: + - max + default_metric: max + - do: + bulk: + refresh: true + index: test + body: + - '{"index": {}}' + - '{"@timestamp": "2021-04-28T18:50:04.467Z", "dim": "A", "agg_metric": {"max": 10}}' + - '{"index": {}}' + - '{"@timestamp": "2021-04-28T18:50:24.467Z", "dim": "B", "agg_metric": {"max": 20}}' + --- load everything: - do: @@ -109,3 +142,26 @@ filter on counter: esql.query: body: query: 'from test | where k8s.pod.network.tx == 1434577921' + +--- +from doc with aggregate_metric_double: + - do: + esql.query: + body: + query: 'from test2' + + - match: {columns.0.name: "@timestamp"} + - match: {columns.0.type: "date"} + - match: {columns.1.name: "agg_metric"} + - match: {columns.1.type: "unsupported"} + - match: {columns.2.name: "dim"} + - match: {columns.2.type: "keyword"} + - length: {values: 0} + +--- +stats on aggregate_metric_double: + - do: + catch: /Cannot use field \[agg_metric\] with unsupported type \[aggregate_metric_double\]/ + esql.query: + body: + query: 'FROM test2 | STATS max(agg_metric) BY dim ' From ddd95d568a1057402c6ef9d479667ad1c1736c83 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 28 Sep 2023 16:16:05 +0100 Subject: [PATCH 129/155] [Transform] Hide the fromVersion method of TransformConfigVersion (#100008) This method wasn't used anywhere, but we don't want to allow anyone to use it in the future. --- .../xpack/core/transform/TransformConfigVersion.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java index acc10008cd40f..3d6a1aef8477a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java @@ -302,7 +302,8 @@ public static TransformConfigVersion max(TransformConfigVersion version1, Transf return version1.id > version2.id ? version1 : version2; } - public static TransformConfigVersion fromVersion(Version version) { + // Visible only for testing + static TransformConfigVersion fromVersion(Version version) { if (version.equals(Version.V_8_10_0)) { return V_10; } From 517a5425c569c5b0c2b5c0be6b6a3810c21f63ff Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Thu, 28 Sep 2023 17:55:00 +0200 Subject: [PATCH 130/155] Deduplicate fields in FieldCaps intra-cluster messages (#100022) We have a stable equality check for individual fields capabilities. Even in the dynamic mapping case where mappings vary across indices, we tend to have a lot of overlap across mappings. By dedplicating the individual field capabilities instances when serializing we can reduce the size of the field caps messages massively in many cases. A repeated field takes up only an extra ~4 bytes in the transport message which compared to tens of MB in the general case. Even if deduplication were to never apply this change should reduce response sizes though as it only adds about ~4 bytes in overhead for a never deduplicated field but saves the redundant double writing of field names we used to do (we wrote them both in the map key and in the value) and it seems safe to assume that almost all field names are longer than 4 bytes. --- .../org/elasticsearch/TransportVersions.java | 2 + .../FieldCapabilitiesIndexResponse.java | 67 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 926cc4801dddc..5f120134acb04 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -145,6 +145,8 @@ static TransportVersion def(int id) { public static final TransportVersion WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED = def(8_502_00_0); public static final TransportVersion RECOVERY_COMMIT_TOO_NEW_EXCEPTION_ADDED = def(8_503_00_0); public static final TransportVersion NODE_INFO_COMPONENT_VERSIONS_ADDED = def(8_504_00_0); + + public static final TransportVersion COMPACT_FIELD_CAPS_ADDED = def(8_505_00_0); /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java index b1f844ed66215..e9e3a05169afc 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java @@ -13,11 +13,13 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -67,6 +69,8 @@ public void writeTo(StreamOutput out) throws IOException { } } + private record CompressedGroup(String[] indices, String mappingHash, int[] fields) {} + static List readList(StreamInput input) throws IOException { if (input.getTransportVersion().before(MAPPING_HASH_VERSION)) { return input.readCollectionAsList(FieldCapabilitiesIndexResponse::new); @@ -77,6 +81,37 @@ static List readList(StreamInput input) throws I responses.add(new FieldCapabilitiesIndexResponse(input)); } final int groups = input.readVInt(); + if (input.getTransportVersion().onOrAfter(TransportVersions.COMPACT_FIELD_CAPS_ADDED)) { + collectCompressedResponses(input, groups, responses); + } else { + collectResponsesLegacyFormat(input, groups, responses); + } + return responses; + } + + private static void collectCompressedResponses(StreamInput input, int groups, ArrayList responses) + throws IOException { + final CompressedGroup[] compressedGroups = new CompressedGroup[groups]; + for (int i = 0; i < groups; i++) { + final String[] indices = input.readStringArray(); + final String mappingHash = input.readString(); + compressedGroups[i] = new CompressedGroup(indices, mappingHash, input.readIntArray()); + } + final IndexFieldCapabilities[] ifcLookup = input.readArray(IndexFieldCapabilities::readFrom, IndexFieldCapabilities[]::new); + for (CompressedGroup compressedGroup : compressedGroups) { + final Map ifc = Maps.newMapWithExpectedSize(compressedGroup.fields.length); + for (int i : compressedGroup.fields) { + var val = ifcLookup[i]; + ifc.put(val.name(), val); + } + for (String index : compressedGroup.indices) { + responses.add(new FieldCapabilitiesIndexResponse(index, compressedGroup.mappingHash, ifc, true)); + } + } + } + + private static void collectResponsesLegacyFormat(StreamInput input, int groups, ArrayList responses) + throws IOException { for (int i = 0; i < groups; i++) { final List indices = input.readStringCollectionAsList(); final String mappingHash = input.readString(); @@ -85,7 +120,6 @@ static List readList(StreamInput input) throws I responses.add(new FieldCapabilitiesIndexResponse(index, mappingHash, ifc, true)); } } - return responses; } static void writeList(StreamOutput output, List responses) throws IOException { @@ -103,7 +137,19 @@ static void writeList(StreamOutput output, List ungroupedResponses.add(r); } } + output.writeCollection(ungroupedResponses); + if (output.getTransportVersion().onOrAfter(TransportVersions.COMPACT_FIELD_CAPS_ADDED)) { + writeCompressedResponses(output, groupedResponsesMap); + } else { + writeResponsesLegacyFormat(output, groupedResponsesMap); + } + } + + private static void writeResponsesLegacyFormat( + StreamOutput output, + Map> groupedResponsesMap + ) throws IOException { output.writeCollection(groupedResponsesMap.values(), (o, fieldCapabilitiesIndexResponses) -> { o.writeCollection(fieldCapabilitiesIndexResponses, (oo, r) -> oo.writeString(r.indexName)); var first = fieldCapabilitiesIndexResponses.get(0); @@ -112,6 +158,25 @@ static void writeList(StreamOutput output, List }); } + private static void writeCompressedResponses(StreamOutput output, Map> groupedResponsesMap) + throws IOException { + final Map fieldDedupMap = new LinkedHashMap<>(); + output.writeCollection(groupedResponsesMap.values(), (o, fieldCapabilitiesIndexResponses) -> { + o.writeCollection(fieldCapabilitiesIndexResponses, (oo, r) -> oo.writeString(r.indexName)); + var first = fieldCapabilitiesIndexResponses.get(0); + o.writeString(first.indexMappingHash); + o.writeVInt(first.responseMap.size()); + for (IndexFieldCapabilities ifc : first.responseMap.values()) { + Integer offset = fieldDedupMap.size(); + final Integer found = fieldDedupMap.putIfAbsent(ifc, offset); + o.writeInt(found == null ? offset : found); + } + }); + // this is a linked hash map so the key-set is written in insertion order, so we can just write it out in order and then read it + // back as an array of FieldCapabilitiesIndexResponse in #collectCompressedResponses to use as a lookup + output.writeCollection(fieldDedupMap.keySet()); + } + /** * Get the index name */ From 246e8324c8531e3821d3498e15477160874fe99a Mon Sep 17 00:00:00 2001 From: David Kyle Date: Thu, 28 Sep 2023 17:34:26 +0100 Subject: [PATCH 131/155] [ML] Handle malformed inference result (#100023) Improved logging and best attempt at handling what is expected to be a rare edge case --- .../process/PyTorchResultProcessor.java | 38 +++++++++++++++---- .../process/PyTorchResultProcessorTests.java | 17 +++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessor.java index 6b92a9349c4ea..4b925464d985b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessor.java @@ -107,17 +107,19 @@ public void process(PyTorchProcess process) { if (result.inferenceResult() != null) { processInferenceResult(result); - } - ThreadSettings threadSettings = result.threadSettings(); - if (threadSettings != null) { - threadSettingsConsumer.accept(threadSettings); + } else if (result.threadSettings() != null) { + threadSettingsConsumer.accept(result.threadSettings()); processThreadSettings(result); - } - if (result.ackResult() != null) { + } else if (result.ackResult() != null) { processAcknowledgement(result); - } - if (result.errorResult() != null) { + } else if (result.errorResult() != null) { processErrorResult(result); + } else { + // will should only get here if the native process + // has produced a partially valid result, one that + // is accepted by the parser but does not have any + // content + handleUnknownResultType(result); } } } catch (Exception e) { @@ -208,6 +210,26 @@ void processErrorResult(PyTorchResult result) { } } + void handleUnknownResultType(PyTorchResult result) { + if (result.requestId() != null) { + PendingResult pendingResult = pendingResults.remove(result.requestId()); + if (pendingResult == null) { + logger.error(() -> format("[%s] no pending result listener for unknown result type [%s]", modelId, result)); + } else { + String msg = format("[%s] pending result listener cannot handle unknown result type [%s]", modelId, result); + logger.error(msg); + var errorResult = new ErrorResult(msg); + pendingResult.listener.onResponse(new PyTorchResult(result.requestId(), null, null, null, null, null, errorResult)); + } + } else { + // Cannot look up the listener without a request id + // all that can be done in this case is log a message. + // The result parser requires a request id so this + // code should not be hit. + logger.error(() -> format("[%s] cannot process unknown result type [%s]", modelId, result)); + } + } + public synchronized ResultStats getResultStats() { long currentMs = currentTimeMsSupplier.getAsLong(); long currentPeriodStartTimeMs = startTime + Intervals.alignToFloor(currentMs - startTime, REPORTING_PERIOD_MS); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessorTests.java index e172f4ffb528c..860da3140f4fe 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/pytorch/process/PyTorchResultProcessorTests.java @@ -153,6 +153,23 @@ public void testPendingRequestAreCalledAtShutdown() { } } + public void testsHandleUnknownResult() { + var processor = new PyTorchResultProcessor("deployment-foo", settings -> {}); + var listener = new AssertingResultListener( + r -> assertThat( + r.errorResult().error(), + containsString("[deployment-foo] pending result listener cannot handle unknown result type") + ) + ); + + processor.registerRequest("no-result-content", listener); + + processor.process( + mockNativeProcess(List.of(new PyTorchResult("no-result-content", null, null, null, null, null, null)).iterator()) + ); + assertTrue(listener.hasResponse); + } + private static class AssertingResultListener implements ActionListener { boolean hasResponse; final Consumer responseAsserter; From 0e219307f2d63bcbfc4357c75bf7c4b510801e21 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 28 Sep 2023 13:17:08 -0400 Subject: [PATCH 132/155] ESQL: Track blocks (#100025) This tracks blocks from topn and a few other places. We're going to try and track blocks all the places. --- .../compute/operator/TopNBenchmark.java | 2 + .../org/elasticsearch/core/Releasables.java | 2 +- .../common/util/BytesRefArray.java | 9 +- .../common/util/MockBigArrays.java | 8 +- .../indices/CrankyCircuitBreakerService.java | 15 +- .../compute/data/BooleanArrayBlock.java | 1 + .../compute/data/BooleanBlockBuilder.java | 16 +- .../compute/data/BooleanVectorBuilder.java | 13 +- .../data/BooleanVectorFixedBuilder.java | 8 + .../compute/data/BytesRefArrayBlock.java | 3 +- .../compute/data/BytesRefArrayVector.java | 2 +- .../compute/data/BytesRefBlockBuilder.java | 29 +++- .../compute/data/BytesRefVectorBuilder.java | 30 +++- .../compute/data/DoubleArrayBlock.java | 1 + .../compute/data/DoubleBlockBuilder.java | 16 +- .../compute/data/DoubleVectorBuilder.java | 13 +- .../data/DoubleVectorFixedBuilder.java | 8 + .../compute/data/IntArrayBlock.java | 1 + .../compute/data/IntBlockBuilder.java | 16 +- .../compute/data/IntVectorBuilder.java | 13 +- .../compute/data/IntVectorFixedBuilder.java | 8 + .../compute/data/LongArrayBlock.java | 1 + .../compute/data/LongBlockBuilder.java | 16 +- .../compute/data/LongVectorBuilder.java | 13 +- .../compute/data/LongVectorFixedBuilder.java | 8 + .../topn/ResultBuilderForBoolean.java | 10 +- .../topn/ResultBuilderForBytesRef.java | 10 +- .../operator/topn/ResultBuilderForDouble.java | 10 +- .../operator/topn/ResultBuilderForInt.java | 10 +- .../operator/topn/ResultBuilderForLong.java | 10 +- .../compute/data/AbstractBlockBuilder.java | 34 ++++ .../compute/data/AbstractVectorBuilder.java | 41 ++++- .../org/elasticsearch/compute/data/Block.java | 6 +- .../compute/data/BlockFactory.java | 4 +- .../compute/data/BlockUtils.java | 2 +- .../compute/data/ConstantNullBlock.java | 14 ++ .../elasticsearch/compute/data/DocBlock.java | 17 +- .../compute/data/ElementType.java | 25 ++- .../elasticsearch/compute/data/Vector.java | 6 +- .../compute/data/X-ArrayBlock.java.st | 3 +- .../compute/data/X-ArrayVector.java.st | 2 +- .../compute/data/X-BlockBuilder.java.st | 55 +++++- .../compute/data/X-VectorBuilder.java.st | 48 +++++- .../compute/data/X-VectorFixedBuilder.java.st | 8 + .../compute/operator/Driver.java | 8 +- .../exchange/ExchangeSinkOperator.java | 4 - .../compute/operator/topn/ResultBuilder.java | 26 ++- .../operator/topn/ResultBuilderForDoc.java | 16 +- .../operator/topn/ResultBuilderForNull.java | 13 +- .../compute/operator/topn/TopNOperator.java | 67 ++++++-- .../operator/topn/X-ResultBuilder.java.st | 10 +- .../elasticsearch/compute/OperatorTests.java | 2 +- .../AggregatorFunctionTestCase.java | 8 +- .../CountAggregatorFunctionTests.java | 5 +- ...istinctBooleanAggregatorFunctionTests.java | 3 +- ...ooleanGroupingAggregatorFunctionTests.java | 3 +- ...stinctBytesRefAggregatorFunctionTests.java | 3 +- ...tesRefGroupingAggregatorFunctionTests.java | 3 +- ...DistinctDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- ...untDistinctIntAggregatorFunctionTests.java | 2 +- ...nctIntGroupingAggregatorFunctionTests.java | 3 +- ...ntDistinctLongAggregatorFunctionTests.java | 4 +- ...ctLongGroupingAggregatorFunctionTests.java | 4 +- .../CountGroupingAggregatorFunctionTests.java | 4 +- .../GroupingAggregatorFunctionTestCase.java | 28 +-- .../MaxDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- .../MaxIntAggregatorFunctionTests.java | 3 +- ...MaxIntGroupingAggregatorFunctionTests.java | 3 +- .../MaxLongAggregatorFunctionTests.java | 5 +- ...axLongGroupingAggregatorFunctionTests.java | 8 +- ...eviationDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- ...teDeviationIntAggregatorFunctionTests.java | 3 +- ...ionIntGroupingAggregatorFunctionTests.java | 3 +- ...eDeviationLongAggregatorFunctionTests.java | 5 +- ...onLongGroupingAggregatorFunctionTests.java | 5 +- .../MinDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- .../MinIntAggregatorFunctionTests.java | 3 +- ...MinIntGroupingAggregatorFunctionTests.java | 3 +- .../MinLongAggregatorFunctionTests.java | 5 +- ...inLongGroupingAggregatorFunctionTests.java | 8 +- ...rcentileDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- .../PercentileIntAggregatorFunctionTests.java | 3 +- ...ileIntGroupingAggregatorFunctionTests.java | 3 +- ...PercentileLongAggregatorFunctionTests.java | 5 +- ...leLongGroupingAggregatorFunctionTests.java | 4 +- .../SumDoubleAggregatorFunctionTests.java | 3 +- ...DoubleGroupingAggregatorFunctionTests.java | 3 +- .../SumIntAggregatorFunctionTests.java | 2 +- ...SumIntGroupingAggregatorFunctionTests.java | 3 +- .../SumLongAggregatorFunctionTests.java | 6 +- ...umLongGroupingAggregatorFunctionTests.java | 4 +- .../compute/data/BlockBuilderTests.java | 146 +++++++++++++--- .../compute/data/BlockFactoryTests.java | 28 ++- .../compute/data/BlockTestUtils.java | 1 + .../data/BytesRefBlockEqualityTests.java | 37 ++-- .../compute/data/DocVectorTests.java | 57 ++++--- .../data/DoubleBlockEqualityTests.java | 37 ++-- .../compute/data/IntBlockEqualityTests.java | 37 ++-- .../compute/data/LongBlockEqualityTests.java | 39 +++-- .../compute/data/TestBlockBuilder.java | 25 +++ .../compute/data/VectorBuilderTests.java | 153 +++++++++++++++++ .../compute/data/VectorFixedBuilderTests.java | 39 +++-- .../ValuesSourceReaderOperatorTests.java | 30 +++- .../operator/AggregationOperatorTests.java | 5 +- .../compute/operator/AnyOperatorTestCase.java | 2 +- .../operator/CannedSourceOperator.java | 25 ++- .../operator/ColumnExtractOperatorTests.java | 3 +- .../compute/operator/EvalOperatorTests.java | 5 +- .../compute/operator/FilterOperatorTests.java | 5 +- .../operator/ForkingOperatorTestCase.java | 12 +- .../HashAggregationOperatorTests.java | 8 +- .../compute/operator/LimitOperatorTests.java | 5 +- .../operator/MvExpandOperatorTests.java | 3 +- .../compute/operator/OperatorTestCase.java | 50 ++++-- .../operator/ProjectOperatorTests.java | 2 +- .../SequenceLongBlockSourceOperator.java | 18 +- .../operator/StringExtractOperatorTests.java | 3 +- .../operator/TupleBlockSourceOperator.java | 12 +- .../compute/operator/topn/ExtractorTests.java | 17 +- .../operator/topn/TopNOperatorTests.java | 160 +++++++++++++++--- .../rest-api-spec/test/50_index_patterns.yml | 59 +++---- .../metadata-ignoreCsvTests.csv-spec | 3 +- .../xpack/esql/action/EsqlActionIT.java | 145 ++++++++-------- .../xpack/esql/action/EsqlDisruptionIT.java | 4 + 129 files changed, 1592 insertions(+), 486 deletions(-) create mode 100644 x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorBuilderTests.java diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/TopNBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/TopNBenchmark.java index 9ef4eef2a6924..84f7cec47b737 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/TopNBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/TopNBenchmark.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DoubleBlock; @@ -107,6 +108,7 @@ private static Operator operator(String data, int topCount) { ClusterSettings.createBuiltInClusterSettings() ); return new TopNOperator( + BlockFactory.getNonBreakingInstance(), breakerService.getBreaker(CircuitBreaker.REQUEST), topCount, elementTypes, diff --git a/libs/core/src/main/java/org/elasticsearch/core/Releasables.java b/libs/core/src/main/java/org/elasticsearch/core/Releasables.java index b8d1a9a542779..c2b48c4706573 100644 --- a/libs/core/src/main/java/org/elasticsearch/core/Releasables.java +++ b/libs/core/src/main/java/org/elasticsearch/core/Releasables.java @@ -89,7 +89,7 @@ private static void close(boolean success, Releasable... releasables) { * // the resources will be released when reaching here * */ - public static Releasable wrap(final Iterable releasables) { + public static Releasable wrap(final Iterable releasables) { return new Releasable() { @Override public void close() { diff --git a/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java b/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java index de061c7f314d6..91dbfc30123fe 100644 --- a/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java +++ b/server/src/main/java/org/elasticsearch/common/util/BytesRefArray.java @@ -160,7 +160,14 @@ public void writeTo(StreamOutput out) throws IOException { @Override public long ramBytesUsed() { - return BASE_RAM_BYTES_USED + startOffsets.ramBytesUsed() + bytes.ramBytesUsed(); + return BASE_RAM_BYTES_USED + bigArraysRamBytesUsed(); + } + + /** + * Memory used by the {@link BigArrays} portion of this {@link BytesRefArray}. + */ + public long bigArraysRamBytesUsed() { + return startOffsets.ramBytesUsed() + bytes.ramBytesUsed(); } } diff --git a/test/framework/src/main/java/org/elasticsearch/common/util/MockBigArrays.java b/test/framework/src/main/java/org/elasticsearch/common/util/MockBigArrays.java index 02e8cfd7f16fc..0a0592b5a01f2 100644 --- a/test/framework/src/main/java/org/elasticsearch/common/util/MockBigArrays.java +++ b/test/framework/src/main/java/org/elasticsearch/common/util/MockBigArrays.java @@ -678,6 +678,9 @@ public void addEstimateBytesAndMaybeBreak(long bytes, String label) throws Circu while (true) { long old = used.get(); long total = old + bytes; + if (total < 0) { + throw new AssertionError("total must be >= 0 but was [" + total + "]"); + } if (total > max.getBytes()) { throw new CircuitBreakingException(ERROR_MESSAGE, bytes, max.getBytes(), Durability.TRANSIENT); } @@ -689,7 +692,10 @@ public void addEstimateBytesAndMaybeBreak(long bytes, String label) throws Circu @Override public void addWithoutBreaking(long bytes) { - used.addAndGet(bytes); + long total = used.addAndGet(bytes); + if (total < 0) { + throw new AssertionError("total must be >= 0 but was [" + total + "]"); + } } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/indices/CrankyCircuitBreakerService.java b/test/framework/src/main/java/org/elasticsearch/indices/CrankyCircuitBreakerService.java index 15ffa52569d00..bd5f974a5f800 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/CrankyCircuitBreakerService.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/CrankyCircuitBreakerService.java @@ -15,6 +15,8 @@ import org.elasticsearch.indices.breaker.CircuitBreakerStats; import org.elasticsearch.test.ESTestCase; +import java.util.concurrent.atomic.AtomicLong; + /** * {@link CircuitBreakerService} that fails one twentieth of the time when you * add bytes. This is useful to make sure code responds sensibly to circuit @@ -27,31 +29,32 @@ public class CrankyCircuitBreakerService extends CircuitBreakerService { public static final String ERROR_MESSAGE = "cranky breaker"; private final CircuitBreaker breaker = new CircuitBreaker() { - @Override - public void circuitBreak(String fieldName, long bytesNeeded) { + private final AtomicLong used = new AtomicLong(); - } + @Override + public void circuitBreak(String fieldName, long bytesNeeded) {} @Override public void addEstimateBytesAndMaybeBreak(long bytes, String label) throws CircuitBreakingException { if (ESTestCase.random().nextInt(20) == 0) { throw new CircuitBreakingException(ERROR_MESSAGE, Durability.PERMANENT); } + used.addAndGet(bytes); } @Override public void addWithoutBreaking(long bytes) { - + used.addAndGet(bytes); } @Override public long getUsed() { - return 0; + return used.get(); } @Override public long getLimit() { - return 0; + return Long.MAX_VALUE; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java index d8a5e471aaf84..b6e36e698355b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanArrayBlock.java @@ -75,6 +75,7 @@ public BooleanBlock expand() { public static long ramBytesEstimated(boolean[] values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java index a7d397fcfb98e..98b9fdb948bc0 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java @@ -7,6 +7,8 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.RamUsageEstimator; + import java.util.Arrays; /** @@ -20,7 +22,7 @@ final class BooleanBlockBuilder extends AbstractBlockBuilder implements BooleanB BooleanBlockBuilder(int estimatedSize, BlockFactory blockFactory) { super(blockFactory); int initialSize = Math.max(estimatedSize, 2); - adjustBreaker(initialSize); + adjustBreaker(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + initialSize * elementSize()); values = new boolean[initialSize]; } @@ -192,8 +194,16 @@ public BooleanBlock build() { block = new BooleanArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); + built(); return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java index 45c74ee6e06d4..3792e39275f82 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java @@ -49,6 +49,7 @@ protected void growValuesArray(int newSize) { @Override public BooleanVector build() { + finish(); BooleanVector vector; if (valueCount == 1) { vector = new ConstantBooleanVector(values[0], 1, blockFactory); @@ -58,8 +59,16 @@ public BooleanVector build() { } vector = new BooleanArrayVector(values, valueCount, blockFactory); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, false); + built(); return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java index 30146d4e55c02..1428a1a221fa3 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorFixedBuilder.java @@ -58,4 +58,12 @@ public BooleanVector build() { } return new BooleanArrayVector(values, values.length, blockFactory); } + + @Override + public void close() { + if (nextIndex >= 0) { + // If nextIndex < 0 we've already built the vector + blockFactory.adjustBreaker(-ramBytesUsed(values.length), false); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java index e4ee70cd27a47..db5b5d3fcf804 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java @@ -77,6 +77,7 @@ public BytesRefBlock expand() { public static long ramBytesEstimated(BytesRefArray values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override @@ -115,7 +116,7 @@ public void close() { throw new IllegalStateException("can't release already released block [" + this + "]"); } released = true; - blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + blockFactory.adjustBreaker(-ramBytesUsed() + values.bigArraysRamBytesUsed(), true); Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java index c8f5276a99db5..1692bfc59358a 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java @@ -85,7 +85,7 @@ public String toString() { @Override public void close() { - blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); + blockFactory.adjustBreaker(-ramBytesUsed() + values.bigArraysRamBytesUsed(), true); Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java index 23c18d2a9ca6e..a60b26667eb79 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java @@ -193,19 +193,42 @@ public BytesRefBlockBuilder mvOrdering(Block.MvOrdering mvOrdering) { public BytesRefBlock build() { finish(); BytesRefBlock block; + assert estimatedBytes == 0 || firstValueIndexes != null; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); Releasables.closeExpectNoException(values); } else { - estimatedBytes += values.ramBytesUsed(); if (isDense() && singleValued()) { block = new BytesRefArrayVector(values, positionCount, blockFactory).asBlock(); } else { block = new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes - values.bigArraysRamBytesUsed(), false); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + values = null; + built(); return block; } + + @Override + public void extraClose() { + Releasables.closeExpectNoException(values); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java index f37ffb2a7e28a..5ea9a2b7d0184 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java @@ -54,16 +54,40 @@ protected void growValuesArray(int newSize) { @Override public BytesRefVector build() { + finish(); BytesRefVector vector; + assert estimatedBytes == 0; if (valueCount == 1) { vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed(), false); Releasables.closeExpectNoException(values); } else { - estimatedBytes = values.ramBytesUsed(); vector = new BytesRefArrayVector(values, valueCount, blockFactory); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - values.bigArraysRamBytesUsed(), false); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + values = null; + built(); return vector; } + + @Override + public void extraClose() { + Releasables.closeExpectNoException(values); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java index b0de974a85c24..675952a8d6a85 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleArrayBlock.java @@ -75,6 +75,7 @@ public DoubleBlock expand() { public static long ramBytesEstimated(double[] values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java index a97f58f3924b1..dca8fe2d0d2e6 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java @@ -7,6 +7,8 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.RamUsageEstimator; + import java.util.Arrays; /** @@ -20,7 +22,7 @@ final class DoubleBlockBuilder extends AbstractBlockBuilder implements DoubleBlo DoubleBlockBuilder(int estimatedSize, BlockFactory blockFactory) { super(blockFactory); int initialSize = Math.max(estimatedSize, 2); - adjustBreaker(initialSize); + adjustBreaker(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + initialSize * elementSize()); values = new double[initialSize]; } @@ -192,8 +194,16 @@ public DoubleBlock build() { block = new DoubleArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); + built(); return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java index f92ec67aec012..12fa06a944fbc 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java @@ -49,6 +49,7 @@ protected void growValuesArray(int newSize) { @Override public DoubleVector build() { + finish(); DoubleVector vector; if (valueCount == 1) { vector = new ConstantDoubleVector(values[0], 1, blockFactory); @@ -58,8 +59,16 @@ public DoubleVector build() { } vector = new DoubleArrayVector(values, valueCount, blockFactory); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, false); + built(); return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java index 83992ed71b720..b636d9eb19756 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorFixedBuilder.java @@ -58,4 +58,12 @@ public DoubleVector build() { } return new DoubleArrayVector(values, values.length, blockFactory); } + + @Override + public void close() { + if (nextIndex >= 0) { + // If nextIndex < 0 we've already built the vector + blockFactory.adjustBreaker(-ramBytesUsed(values.length), false); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java index 7a345941df019..4170009b89ab2 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntArrayBlock.java @@ -75,6 +75,7 @@ public IntBlock expand() { public static long ramBytesEstimated(int[] values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java index 53d379d715c9b..ba96f85e73197 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java @@ -7,6 +7,8 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.RamUsageEstimator; + import java.util.Arrays; /** @@ -20,7 +22,7 @@ final class IntBlockBuilder extends AbstractBlockBuilder implements IntBlock.Bui IntBlockBuilder(int estimatedSize, BlockFactory blockFactory) { super(blockFactory); int initialSize = Math.max(estimatedSize, 2); - adjustBreaker(initialSize); + adjustBreaker(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + initialSize * elementSize()); values = new int[initialSize]; } @@ -192,8 +194,16 @@ public IntBlock build() { block = new IntArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); + built(); return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java index 0533d5463a4e7..155adfec02b9f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java @@ -49,6 +49,7 @@ protected void growValuesArray(int newSize) { @Override public IntVector build() { + finish(); IntVector vector; if (valueCount == 1) { vector = new ConstantIntVector(values[0], 1, blockFactory); @@ -58,8 +59,16 @@ public IntVector build() { } vector = new IntArrayVector(values, valueCount, blockFactory); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, false); + built(); return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java index 19303b4024869..03a15fb10a800 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorFixedBuilder.java @@ -58,4 +58,12 @@ public IntVector build() { } return new IntArrayVector(values, values.length, blockFactory); } + + @Override + public void close() { + if (nextIndex >= 0) { + // If nextIndex < 0 we've already built the vector + blockFactory.adjustBreaker(-ramBytesUsed(values.length), false); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java index 21c6b445cd37d..778ec4294180c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongArrayBlock.java @@ -75,6 +75,7 @@ public LongBlock expand() { public static long ramBytesEstimated(long[] values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java index a378b382ce31e..09d858e7c9b03 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java @@ -7,6 +7,8 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.RamUsageEstimator; + import java.util.Arrays; /** @@ -20,7 +22,7 @@ final class LongBlockBuilder extends AbstractBlockBuilder implements LongBlock.B LongBlockBuilder(int estimatedSize, BlockFactory blockFactory) { super(blockFactory); int initialSize = Math.max(estimatedSize, 2); - adjustBreaker(initialSize); + adjustBreaker(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + initialSize * elementSize()); values = new long[initialSize]; } @@ -192,8 +194,16 @@ public LongBlock build() { block = new LongArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); + built(); return block; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java index 6b2e9f1de7d51..3b8bbf4219d00 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java @@ -49,6 +49,7 @@ protected void growValuesArray(int newSize) { @Override public LongVector build() { + finish(); LongVector vector; if (valueCount == 1) { vector = new ConstantLongVector(values[0], 1, blockFactory); @@ -58,8 +59,16 @@ public LongVector build() { } vector = new LongArrayVector(values, valueCount, blockFactory); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, false); + built(); return vector; } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java index 5414b7669f588..0960d607d9c0d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorFixedBuilder.java @@ -58,4 +58,12 @@ public LongVector build() { } return new LongArrayVector(values, values.length, blockFactory); } + + @Override + public void close() { + if (nextIndex >= 0) { + // If nextIndex < 0 we've already built the vector + blockFactory.adjustBreaker(-ramBytesUsed(values.length), false); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBoolean.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBoolean.java index 50cef0417dd45..3d568adc2b5ea 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBoolean.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBoolean.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; class ResultBuilderForBoolean implements ResultBuilder { @@ -20,10 +21,10 @@ class ResultBuilderForBoolean implements ResultBuilder { */ private boolean key; - ResultBuilderForBoolean(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderForBoolean(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { assert encoder == TopNEncoder.DEFAULT_UNSORTABLE : encoder.toString(); this.inKey = inKey; - this.builder = BooleanBlock.newBlockBuilder(initialSize); + this.builder = BooleanBlock.newBlockBuilder(initialSize, blockFactory); } @Override @@ -63,4 +64,9 @@ public BooleanBlock build() { public String toString() { return "ResultBuilderForBoolean[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBytesRef.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBytesRef.java index 55f324c931b67..e37f82f3363a9 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBytesRef.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForBytesRef.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; class ResultBuilderForBytesRef implements ResultBuilder { @@ -24,10 +25,10 @@ class ResultBuilderForBytesRef implements ResultBuilder { */ private BytesRef key; - ResultBuilderForBytesRef(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderForBytesRef(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { this.encoder = encoder; this.inKey = inKey; - this.builder = BytesRefBlock.newBlockBuilder(initialSize); + this.builder = BytesRefBlock.newBlockBuilder(initialSize, blockFactory); } @Override @@ -67,4 +68,9 @@ public BytesRefBlock build() { public String toString() { return "ResultBuilderForBytesRef[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForDouble.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForDouble.java index ed4a9b45d90dc..77c976c6e0085 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForDouble.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForDouble.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; class ResultBuilderForDouble implements ResultBuilder { @@ -20,10 +21,10 @@ class ResultBuilderForDouble implements ResultBuilder { */ private double key; - ResultBuilderForDouble(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderForDouble(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { assert encoder == TopNEncoder.DEFAULT_UNSORTABLE : encoder.toString(); this.inKey = inKey; - this.builder = DoubleBlock.newBlockBuilder(initialSize); + this.builder = DoubleBlock.newBlockBuilder(initialSize, blockFactory); } @Override @@ -63,4 +64,9 @@ public DoubleBlock build() { public String toString() { return "ResultBuilderForDouble[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForInt.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForInt.java index 2bcfc81107445..389ed3bc2e3c3 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForInt.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForInt.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; class ResultBuilderForInt implements ResultBuilder { @@ -20,10 +21,10 @@ class ResultBuilderForInt implements ResultBuilder { */ private int key; - ResultBuilderForInt(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderForInt(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { assert encoder == TopNEncoder.DEFAULT_UNSORTABLE : encoder.toString(); this.inKey = inKey; - this.builder = IntBlock.newBlockBuilder(initialSize); + this.builder = IntBlock.newBlockBuilder(initialSize, blockFactory); } @Override @@ -63,4 +64,9 @@ public IntBlock build() { public String toString() { return "ResultBuilderForInt[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForLong.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForLong.java index 3ada85bf9d5c9..63ee9d35c59e5 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForLong.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/ResultBuilderForLong.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; class ResultBuilderForLong implements ResultBuilder { @@ -20,10 +21,10 @@ class ResultBuilderForLong implements ResultBuilder { */ private long key; - ResultBuilderForLong(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderForLong(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { assert encoder == TopNEncoder.DEFAULT_UNSORTABLE : encoder.toString(); this.inKey = inKey; - this.builder = LongBlock.newBlockBuilder(initialSize); + this.builder = LongBlock.newBlockBuilder(initialSize, blockFactory); } @Override @@ -63,4 +64,9 @@ public LongBlock build() { public String toString() { return "ResultBuilderForLong[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java index a6ad5d1299543..3d06eba398513 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java @@ -33,6 +33,8 @@ abstract class AbstractBlockBuilder implements Block.Builder { /** The number of bytes currently estimated with the breaker. */ protected long estimatedBytes; + private boolean closed = false; + protected AbstractBlockBuilder(BlockFactory blockFactory) { this.blockFactory = blockFactory; } @@ -101,7 +103,14 @@ protected final void updatePosition() { } } + /** + * Called during implementations of {@link Block.Builder#build} as a first step + * to check if the block is still open and to finish the last position. + */ protected final void finish() { + if (closed) { + throw new IllegalStateException("already closed"); + } if (positionEntryIsOpen) { endPositionEntry(); } @@ -110,6 +119,16 @@ protected final void finish() { } } + /** + * Called during implementations of {@link Block.Builder#build} as a last step + * to mark the Builder as closed and make sure that further closes don't double + * free memory. + */ + protected final void built() { + closed = true; + estimatedBytes = 0; + } + protected abstract void growValuesArray(int newSize); /** The number of bytes used to represent each value element. */ @@ -125,6 +144,20 @@ protected final void ensureCapacity() { growValuesArray(newSize); } + @Override + public final void close() { + if (closed == false) { + closed = true; + adjustBreaker(-estimatedBytes); + extraClose(); + } + } + + /** + * Called when first {@link #close() closed}. + */ + protected void extraClose() {} + static int calculateNewArraySize(int currentSize) { // trivially, grows array by 50% return currentSize + (currentSize >> 1); @@ -133,6 +166,7 @@ static int calculateNewArraySize(int currentSize) { protected void adjustBreaker(long deltaBytes) { blockFactory.adjustBreaker(deltaBytes, false); estimatedBytes += deltaBytes; + assert estimatedBytes >= 0; } private void setFirstValue(int position, int value) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java index 49ce276074735..274e88cd8d8b6 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBuilder.java @@ -7,9 +7,14 @@ package org.elasticsearch.compute.data; -abstract class AbstractVectorBuilder { +abstract class AbstractVectorBuilder implements Vector.Builder { protected int valueCount; + /** + * Has this builder been closed already? + */ + private boolean closed = false; + protected final BlockFactory blockFactory; /** The number of bytes currently estimated with the breaker. */ @@ -46,4 +51,38 @@ protected void adjustBreaker(long deltaBytes) { blockFactory.adjustBreaker(deltaBytes, false); estimatedBytes += deltaBytes; } + + /** + * Called during implementations of {@link Block.Builder#build} as a first step + * to check if the block is still open and to finish the last position. + */ + protected final void finish() { + if (closed) { + throw new IllegalStateException("already closed"); + } + } + + /** + * Called during implementations of {@link Block.Builder#build} as a last step + * to mark the Builder as closed and make sure that further closes don't double + * free memory. + */ + protected final void built() { + closed = true; + estimatedBytes = 0; + } + + @Override + public final void close() { + if (closed == false) { + closed = true; + adjustBreaker(-estimatedBytes); + extraClose(); + } + } + + /** + * Called when first {@link #close() closed}. + */ + protected void extraClose() {} } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index 5b10a3a510de0..ca0721ebbe4f8 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -133,7 +133,11 @@ static Block constantNullBlock(int positions, BlockFactory blockFactory) { return blockFactory.newConstantNullBlock(positions); } - interface Builder { + /** + * Builds {@link Block}s. Typically, you use one of it's direct supinterfaces like {@link IntBlock.Builder}. + * This is {@link Releasable} and should be released after building the block or if building the block fails. + */ + interface Builder extends Releasable { /** * Appends a null value to the block. diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java index 2afea228a4a78..63de604c49b18 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java @@ -305,7 +305,7 @@ public BytesRefBlock newBytesRefArrayBlock( MvOrdering mvOrdering ) { var b = new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); - adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); + adjustBreaker(b.ramBytesUsed() - values.bigArraysRamBytesUsed(), true); return b; } @@ -315,7 +315,7 @@ public BytesRefVector.Builder newBytesRefVectorBuilder(int estimatedSize) { public BytesRefVector newBytesRefArrayVector(BytesRefArray values, int positionCount) { var b = new BytesRefArrayVector(values, positionCount, this); - adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); + adjustBreaker(b.ramBytesUsed() - values.bigArraysRamBytesUsed(), true); return b; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java index 2ebbb771b5df1..0d09f7bb480e0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java @@ -210,7 +210,7 @@ public static Object toJavaObject(Block block, int position) { private static Object valueAtOffset(Block block, int offset) { return switch (block.elementType()) { case BOOLEAN -> ((BooleanBlock) block).getBoolean(offset); - case BYTES_REF -> ((BytesRefBlock) block).getBytesRef(offset, new BytesRef()); + case BYTES_REF -> BytesRef.deepCopyOf(((BytesRefBlock) block).getBytesRef(offset, new BytesRef())); case DOUBLE -> ((DoubleBlock) block).getDouble(offset); case INT -> ((IntBlock) block).getInt(offset); case LONG -> ((LongBlock) block).getLong(offset); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java index 2da9cfeba09f0..01994af1cfc96 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java @@ -136,6 +136,11 @@ public void close() { static class Builder implements Block.Builder { private int positionCount; + /** + * Has this builder been closed already? + */ + private boolean closed = false; + @Override public Builder appendNull() { positionCount++; @@ -174,7 +179,16 @@ public Block.Builder mvOrdering(MvOrdering mvOrdering) { @Override public Block build() { + if (closed) { + throw new IllegalStateException("already closed"); + } + close(); return new ConstantNullBlock(positionCount); } + + @Override + public void close() { + closed = true; + } } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java index b21a956980f6a..6bcf913ce6240 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java @@ -82,8 +82,8 @@ public void close() { /** * A builder the for {@link DocBlock}. */ - public static Builder newBlockBuilder(int estimatedSize) { - return new Builder(estimatedSize); + public static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return new Builder(estimatedSize, blockFactory); } public static class Builder implements Block.Builder { @@ -91,10 +91,10 @@ public static class Builder implements Block.Builder { private final IntVector.Builder segments; private final IntVector.Builder docs; - private Builder(int estimatedSize) { - shards = IntVector.newVectorBuilder(estimatedSize); - segments = IntVector.newVectorBuilder(estimatedSize); - docs = IntVector.newVectorBuilder(estimatedSize); + private Builder(int estimatedSize, BlockFactory blockFactory) { + shards = IntVector.newVectorBuilder(estimatedSize, blockFactory); + segments = IntVector.newVectorBuilder(estimatedSize, blockFactory); + docs = IntVector.newVectorBuilder(estimatedSize, blockFactory); } public Builder appendShard(int shard) { @@ -153,5 +153,10 @@ public DocBlock build() { // Pass null for singleSegmentNonDecreasing so we calculate it when we first need it. return new DocVector(shards.build(), segments.build(), docs.build(), null).asBlock(); } + + @Override + public void close() { + Releasables.closeExpectNoException(shards, segments, docs); + } } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ElementType.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ElementType.java index 0c85d433018e0..4467766a9e0ef 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ElementType.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ElementType.java @@ -9,8 +9,6 @@ import org.apache.lucene.util.BytesRef; -import java.util.function.IntFunction; - /** * The type of elements in {@link Block} and {@link Vector} */ @@ -22,7 +20,7 @@ public enum ElementType { /** * Blocks containing only null values. */ - NULL(estimatedSize -> new ConstantNullBlock.Builder()), + NULL((estimatedSize, blockFactory) -> new ConstantNullBlock.Builder()), BYTES_REF(BytesRefBlock::newBlockBuilder), @@ -34,19 +32,32 @@ public enum ElementType { /** * Intermediate blocks which don't support retrieving elements. */ - UNKNOWN(estimatedSize -> { throw new UnsupportedOperationException("can't build null blocks"); }); + UNKNOWN((estimatedSize, blockFactory) -> { throw new UnsupportedOperationException("can't build null blocks"); }); + + interface BuilderSupplier { + Block.Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory); + } - private final IntFunction builder; + private final BuilderSupplier builder; - ElementType(IntFunction builder) { + ElementType(BuilderSupplier builder) { this.builder = builder; } /** * Create a new {@link Block.Builder} for blocks of this type. + * @deprecated use {@link #newBlockBuilder(int, BlockFactory)} */ + @Deprecated public Block.Builder newBlockBuilder(int estimatedSize) { - return builder.apply(estimatedSize); + return builder.newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); + } + + /** + * Create a new {@link Block.Builder} for blocks of this type. + */ + public Block.Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { + return builder.newBlockBuilder(estimatedSize, blockFactory); } public static ElementType fromJava(Class type) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java index 171bdbd62f4d0..c9ecf1aa9e399 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Vector.java @@ -50,7 +50,11 @@ public interface Vector extends Accountable, Releasable { /** The block factory associated with this vector. */ BlockFactory blockFactory(); - interface Builder { + /** + * Builds {@link Vector}s. Typically, you use one of it's direct supinterfaces like {@link IntVector.Builder}. + * This is {@link Releasable} and should be released after building the vector or if building the vector fails. + */ + interface Builder extends Releasable { /** * Builds the block. This method can be called multiple times. */ diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st index 10ff868c09806..ddb0eced039be 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st @@ -93,6 +93,7 @@ $endif$ public static long ramBytesEstimated($if(BytesRef)$BytesRefArray$else$$type$[]$endif$ values, int[] firstValueIndexes, BitSet nullsMask) { return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(values) + BlockRamUsageEstimator.sizeOf(firstValueIndexes) + BlockRamUsageEstimator.sizeOfBitSet(nullsMask) + RamUsageEstimator.shallowSizeOfInstance(MvOrdering.class); + // TODO mvordering is shared } @Override @@ -137,7 +138,7 @@ $endif$ } released = true; $if(BytesRef)$ - blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + blockFactory.adjustBreaker(-ramBytesUsed() + values.bigArraysRamBytesUsed(), true); Releasables.closeExpectNoException(values); $else$ blockFactory.adjustBreaker(-ramBytesUsed(), true); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st index b6a8714f882ee..3e6ccc2286675 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st @@ -110,7 +110,7 @@ $endif$ $if(BytesRef)$ @Override public void close() { - blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); + blockFactory.adjustBreaker(-ramBytesUsed() + values.bigArraysRamBytesUsed(), true); Releasables.closeExpectNoException(values); } $endif$ diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st index 4d43f25577cc5..0ccfc45f18664 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st @@ -14,6 +14,8 @@ import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.core.Releasables; $else$ +import org.apache.lucene.util.RamUsageEstimator; + import java.util.Arrays; $endif$ @@ -41,7 +43,7 @@ $else$ $Type$BlockBuilder(int estimatedSize, BlockFactory blockFactory) { super(blockFactory); int initialSize = Math.max(estimatedSize, 2); - adjustBreaker(initialSize); + adjustBreaker(RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + initialSize * elementSize()); values = new $type$[initialSize]; } $endif$ @@ -246,27 +248,68 @@ $endif$ public $Type$Block build() { finish(); $Type$Block block; - if (hasNonNullValue && positionCount == 1 && valueCount == 1) { $if(BytesRef)$ + assert estimatedBytes == 0 || firstValueIndexes != null; + if (hasNonNullValue && positionCount == 1 && valueCount == 1) { block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); Releasables.closeExpectNoException(values); } else { - estimatedBytes += values.ramBytesUsed(); + if (isDense() && singleValued()) { + block = new $Type$ArrayVector(values, positionCount, blockFactory).asBlock(); + } else { + block = new $Type$ArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); + } + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes - values.bigArraysRamBytesUsed(), false); + } + values = null; $else$ + if (hasNonNullValue && positionCount == 1 && valueCount == 1) { block = new Constant$Type$Vector(values[0], 1, blockFactory).asBlock(); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } -$endif$ if (isDense() && singleValued()) { block = new $Type$ArrayVector(values, positionCount, blockFactory).asBlock(); } else { block = new $Type$ArrayBlock(values, positionCount, firstValueIndexes, nullsMask, mvOrdering, blockFactory); } } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, false); +$endif$ + built(); return block; } +$if(BytesRef)$ + + @Override + public void extraClose() { + Releasables.closeExpectNoException(values); + } +$endif$ } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st index b813120b42e43..1e243c49b5d82 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st @@ -83,24 +83,62 @@ $endif$ @Override public $Type$Vector build() { + finish(); $Type$Vector vector; - if (valueCount == 1) { $if(BytesRef)$ + assert estimatedBytes == 0; + if (valueCount == 1) { vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed(), false); Releasables.closeExpectNoException(values); } else { - estimatedBytes = values.ramBytesUsed(); + vector = new $Type$ArrayVector(values, valueCount, blockFactory); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - values.bigArraysRamBytesUsed(), false); + } + values = null; $else$ + if (valueCount == 1) { vector = new Constant$Type$Vector(values[0], 1, blockFactory); } else { if (values.length - valueCount > 1024 || valueCount < (values.length / 2)) { values = Arrays.copyOf(values, valueCount); } -$endif$ vector = new $Type$ArrayVector(values, valueCount, blockFactory); } - // update the breaker with the actual bytes used. - blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); + /* + * Update the breaker with the actual bytes used. + * We pass false below even though we've used the bytes. That's weird, + * but if we break here we will throw away the used memory, letting + * it be deallocated. The exception will bubble up and the builder will + * still technically be open, meaning the calling code should close it + * which will return all used memory to the breaker. + */ + blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, false); +$endif$ + built(); return vector; } +$if(BytesRef)$ + + @Override + public void extraClose() { + Releasables.closeExpectNoException(values); + } +$endif$ } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st index 86bc6b0a095d6..d732c85db7467 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorFixedBuilder.java.st @@ -58,4 +58,12 @@ final class $Type$VectorFixedBuilder implements $Type$Vector.FixedBuilder { } return new $Type$ArrayVector(values, values.length, blockFactory); } + + @Override + public void close() { + if (nextIndex >= 0) { + // If nextIndex < 0 we've already built the vector + blockFactory.adjustBreaker(-ramBytesUsed(values.length), false); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/Driver.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/Driver.java index 281693a487255..1a1604406892c 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/Driver.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/Driver.java @@ -181,7 +181,13 @@ private SubscribableListener runSingleLoopIteration() { if (op.isFinished() == false && nextOp.needsInput()) { Page page = op.getOutput(); - if (page != null && page.getPositionCount() != 0) { + if (page == null) { + // No result, just move to the next iteration + } else if (page.getPositionCount() == 0) { + // Empty result, release any memory it holds immediately and move to the next iteration + page.releaseBlocks(); + } else { + // Non-empty result from the previous operation, move it to the next operation nextOp.addInput(page); movedPage = true; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSinkOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSinkOperator.java index d52f25e9d8306..0fb6ec6f63d96 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSinkOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSinkOperator.java @@ -36,10 +36,6 @@ public record ExchangeSinkOperatorFactory(Supplier exchangeSinks, implements SinkOperatorFactory { - public ExchangeSinkOperatorFactory(Supplier exchangeSinks) { - this(exchangeSinks, Function.identity()); - } - @Override public SinkOperator get(DriverContext driverContext) { return new ExchangeSinkOperator(exchangeSinks.get(), transformer); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilder.java index b8a41a3ee343d..bd2027cade78f 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilder.java @@ -9,12 +9,14 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.core.Releasable; /** * Builds {@link Block}s from keys and values encoded into {@link BytesRef}s. */ -interface ResultBuilder { +interface ResultBuilder extends Releasable { /** * Called for each sort key before {@link #decodeValue} to consume the sort key and * store the value of the key for {@link #decodeValue} can use it to reconstruct @@ -36,15 +38,21 @@ interface ResultBuilder { */ Block build(); - static ResultBuilder resultBuilderFor(ElementType elementType, TopNEncoder encoder, boolean inKey, int positions) { + static ResultBuilder resultBuilderFor( + BlockFactory blockFactory, + ElementType elementType, + TopNEncoder encoder, + boolean inKey, + int positions + ) { return switch (elementType) { - case BOOLEAN -> new ResultBuilderForBoolean(encoder, inKey, positions); - case BYTES_REF -> new ResultBuilderForBytesRef(encoder, inKey, positions); - case INT -> new ResultBuilderForInt(encoder, inKey, positions); - case LONG -> new ResultBuilderForLong(encoder, inKey, positions); - case DOUBLE -> new ResultBuilderForDouble(encoder, inKey, positions); - case NULL -> new ResultBuilderForNull(); - case DOC -> new ResultBuilderForDoc(positions); + case BOOLEAN -> new ResultBuilderForBoolean(blockFactory, encoder, inKey, positions); + case BYTES_REF -> new ResultBuilderForBytesRef(blockFactory, encoder, inKey, positions); + case INT -> new ResultBuilderForInt(blockFactory, encoder, inKey, positions); + case LONG -> new ResultBuilderForLong(blockFactory, encoder, inKey, positions); + case DOUBLE -> new ResultBuilderForDouble(blockFactory, encoder, inKey, positions); + case NULL -> new ResultBuilderForNull(blockFactory); + case DOC -> new ResultBuilderForDoc(blockFactory, positions); default -> { assert false : "Result builder for [" + elementType + "]"; throw new UnsupportedOperationException("Result builder for [" + elementType + "]"); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java index 166d5be83b474..7fb507ffdbead 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForDoc.java @@ -13,12 +13,15 @@ import org.elasticsearch.compute.data.DocVector; class ResultBuilderForDoc implements ResultBuilder { + private final BlockFactory blockFactory; private final int[] shards; private final int[] segments; private final int[] docs; private int position; - ResultBuilderForDoc(int positions) { + ResultBuilderForDoc(BlockFactory blockFactory, int positions) { + // TODO use fixed length builders + this.blockFactory = blockFactory; this.shards = new int[positions]; this.segments = new int[positions]; this.docs = new int[positions]; @@ -40,9 +43,9 @@ public void decodeValue(BytesRef values) { @Override public Block build() { return new DocVector( - BlockFactory.getNonBreakingInstance().newIntArrayVector(shards, position), - BlockFactory.getNonBreakingInstance().newIntArrayVector(segments, position), - BlockFactory.getNonBreakingInstance().newIntArrayVector(docs, position), + blockFactory.newIntArrayVector(shards, position), + blockFactory.newIntArrayVector(segments, position), + blockFactory.newIntArrayVector(docs, position), null ).asBlock(); } @@ -51,4 +54,9 @@ public Block build() { public String toString() { return "ValueExtractorForDoc"; } + + @Override + public void close() { + // TODO memory accounting + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForNull.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForNull.java index 05b9ba2a07658..a45f16fc30910 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForNull.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/ResultBuilderForNull.java @@ -9,10 +9,16 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; public class ResultBuilderForNull implements ResultBuilder { + private final BlockFactory blockFactory; private int positions; + public ResultBuilderForNull(BlockFactory blockFactory) { + this.blockFactory = blockFactory; + } + @Override public void decodeKey(BytesRef keys) { throw new AssertionError("somehow got a value for a null key"); @@ -29,11 +35,16 @@ public void decodeValue(BytesRef values) { @Override public Block build() { - return Block.constantNullBlock(positions); + return Block.constantNullBlock(positions, blockFactory); } @Override public String toString() { return "ValueExtractorForNull"; } + + @Override + public void close() { + // Nothing to close + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java index 86b3a18992db4..9657d60376763 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/TopNOperator.java @@ -12,7 +12,9 @@ import org.apache.lucene.util.PriorityQueue; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -205,7 +207,15 @@ public record TopNOperatorFactory( @Override public TopNOperator get(DriverContext driverContext) { - return new TopNOperator(driverContext.breaker(), topCount, elementTypes, encoders, sortOrders, maxPageSize); + return new TopNOperator( + driverContext.blockFactory(), + driverContext.breaker(), + topCount, + elementTypes, + encoders, + sortOrders, + maxPageSize + ); } @Override @@ -222,6 +232,7 @@ public String describe() { } } + private final BlockFactory blockFactory; private final CircuitBreaker breaker; private final Queue inputQueue; @@ -231,9 +242,11 @@ public String describe() { private final List encoders; private final List sortOrders; + private Row spare; private Iterator output; public TopNOperator( + BlockFactory blockFactory, CircuitBreaker breaker, int topCount, List elementTypes, @@ -241,6 +254,7 @@ public TopNOperator( List sortOrders, int maxPageSize ) { + this.blockFactory = blockFactory; this.breaker = breaker; this.maxPageSize = maxPageSize; this.elementTypes = elementTypes; @@ -301,21 +315,20 @@ public void addInput(Page page) { * and must be closed. That happens either because it's overflow from the * inputQueue or because we hit an allocation failure while building it. */ - Row row = null; try { for (int i = 0; i < page.getPositionCount(); i++) { - if (row == null) { - row = new Row(breaker); + if (spare == null) { + spare = new Row(breaker); } else { - row.keys.clear(); - row.orderByCompositeKeyAscending.clear(); - row.values.clear(); + spare.keys.clear(); + spare.orderByCompositeKeyAscending.clear(); + spare.values.clear(); } - rowFiller.row(i, row); - row = inputQueue.insertWithOverflow(row); + rowFiller.row(i, spare); + spare = inputQueue.insertWithOverflow(spare); } } finally { - Releasables.close(row); + Releasables.close(() -> page.releaseBlocks()); } } @@ -327,18 +340,24 @@ public void finish() { } private Iterator toPages() { + if (spare != null) { + // Remove the spare, we're never going to use it again. + spare.close(); + spare = null; + } if (inputQueue.size() == 0) { return Collections.emptyIterator(); } List list = new ArrayList<>(inputQueue.size()); + List result = new ArrayList<>(); + ResultBuilder[] builders = null; + boolean success = false; try { while (inputQueue.size() > 0) { list.add(inputQueue.pop()); } Collections.reverse(list); - List result = new ArrayList<>(); - ResultBuilder[] builders = null; int p = 0; int size = 0; for (int i = 0; i < list.size(); i++) { @@ -347,6 +366,7 @@ private Iterator toPages() { builders = new ResultBuilder[elementTypes.size()]; for (int b = 0; b < builders.length; b++) { builders[b] = ResultBuilder.resultBuilderFor( + blockFactory, elementTypes.get(b), encoders.get(b).toUnsortable(), channelInKey(sortOrders, b), @@ -386,14 +406,22 @@ private Iterator toPages() { p++; if (p == size) { result.add(new Page(Arrays.stream(builders).map(ResultBuilder::build).toArray(Block[]::new))); + Releasables.closeExpectNoException(builders); builders = null; } - } assert builders == null; + success = true; return result.iterator(); } finally { - Releasables.closeExpectNoException(() -> Releasables.close(list)); + if (success == false) { + List close = new ArrayList<>(list); + for (Page p : result) { + close.add(p::releaseBlocks); + } + Collections.addAll(close, builders); + Releasables.closeExpectNoException(Releasables.wrap(close)); + } } } @@ -422,10 +450,15 @@ public Page getOutput() { @Override public void close() { /* - * If everything went well we'll have drained inputQueue to this'll - * be a noop. But if inputQueue + * If we close before calling finish then spare and inputQueue will be live rows + * that need closing. If we close after calling finish then the output iterator + * will contain pages of results that have yet to be returned. */ - Releasables.closeExpectNoException(() -> Releasables.close(inputQueue)); + Releasables.closeExpectNoException( + spare, + inputQueue == null ? null : Releasables.wrap(inputQueue), + output == null ? null : Releasables.wrap(() -> Iterators.map(output, p -> p::releaseBlocks)) + ); } private static long SHALLOW_SIZE = RamUsageEstimator.shallowSizeOfInstance(TopNOperator.class) + RamUsageEstimator diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-ResultBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-ResultBuilder.java.st index 5f9a35bd0ebd3..ebe62398c8504 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-ResultBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-ResultBuilder.java.st @@ -8,6 +8,7 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.$Type$Block; class ResultBuilderFor$Type$ implements ResultBuilder { @@ -26,14 +27,14 @@ $endif$ */ private $type$ key; - ResultBuilderFor$Type$(TopNEncoder encoder, boolean inKey, int initialSize) { + ResultBuilderFor$Type$(BlockFactory blockFactory, TopNEncoder encoder, boolean inKey, int initialSize) { $if(BytesRef)$ this.encoder = encoder; $else$ assert encoder == TopNEncoder.DEFAULT_UNSORTABLE : encoder.toString(); $endif$ this.inKey = inKey; - this.builder = $Type$Block.newBlockBuilder(initialSize); + this.builder = $Type$Block.newBlockBuilder(initialSize, blockFactory); } @Override @@ -81,4 +82,9 @@ $endif$ public String toString() { return "ResultBuilderFor$Type$[inKey=" + inKey + "]"; } + + @Override + public void close() { + builder.close(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java index 04a966b399870..bdf696f460060 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java @@ -287,7 +287,7 @@ public void testLimitOperator() { try ( var driver = new Driver( driverContext, - new SequenceLongBlockSourceOperator(values, 100), + new SequenceLongBlockSourceOperator(driverContext.blockFactory(), values, 100), List.of((new LimitOperator.Factory(limit)).get(driverContext)), new PageConsumerOperator(page -> { LongBlock block = page.getBlock(0); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java index a4b6c8b965962..22325039af124 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java @@ -91,8 +91,8 @@ protected final ByteSizeValue smallEnoughToCircuitBreak() { public final void testIgnoresNulls() { int end = between(1_000, 100_000); List results = new ArrayList<>(); - List input = CannedSourceOperator.collectPages(simpleInput(end)); DriverContext driverContext = driverContext(); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), end)); try ( Driver d = new Driver( @@ -111,7 +111,9 @@ public final void testIgnoresNulls() { public final void testMultivalued() { int end = between(1_000, 100_000); DriverContext driverContext = driverContext(); - List input = CannedSourceOperator.collectPages(new PositionMergingSourceOperator(simpleInput(end))); + List input = CannedSourceOperator.collectPages( + new PositionMergingSourceOperator(simpleInput(driverContext.blockFactory(), end)) + ); assertSimpleOutput(input, drive(simple(BigArrays.NON_RECYCLING_INSTANCE).get(driverContext), input.iterator())); } @@ -119,7 +121,7 @@ public final void testMultivaluedWithNulls() { int end = between(1_000, 100_000); DriverContext driverContext = driverContext(); List input = CannedSourceOperator.collectPages( - new NullInsertingSourceOperator(new PositionMergingSourceOperator(simpleInput(end))) + new NullInsertingSourceOperator(new PositionMergingSourceOperator(simpleInput(driverContext.blockFactory(), end))) ); assertSimpleOutput(input, drive(simple(BigArrays.NON_RECYCLING_INSTANCE).get(driverContext), input.iterator())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountAggregatorFunctionTests.java index 11241020a6709..623de7fdd1fff 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -21,8 +22,8 @@ public class CountAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLong())); + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLong())); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunctionTests.java index 74cd88feed3f4..febbb4d4a0615 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.SequenceBooleanBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -21,7 +22,7 @@ public class CountDistinctBooleanAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceBooleanBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> randomBoolean()).toList()); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunctionTests.java index eab1b9cb2d8de..7360b101bf79d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongBooleanTupleBlockSourceOperator; @@ -33,7 +34,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongBooleanTupleBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomGroupId(size), randomBoolean())) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunctionTests.java index 69ccc0a04c0f9..c495a6b9f196b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunctionTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.BytesRefBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -23,7 +24,7 @@ public class CountDistinctBytesRefAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, Math.min(Integer.MAX_VALUE, Integer.MAX_VALUE / size)); return new BytesRefBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> new BytesRef(String.valueOf(between(-max, max)))).toList() diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunctionTests.java index 919d06af430fd..eadbba9f91880 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongBytesRefTupleBlockSourceOperator; @@ -35,7 +36,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongBytesRefTupleBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomGroupId(size), new BytesRef(String.valueOf(between(1, 10000))))) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunctionTests.java index c0678441cdc74..ccfe7b426ebca 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -23,7 +24,7 @@ public class CountDistinctDoubleAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceDoubleBlockSourceOperator(LongStream.range(0, size).mapToDouble(l -> ESTestCase.randomDouble())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunctionTests.java index 5a928f12d33b7..0c4d89da09b99 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -34,7 +35,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongDoubleTupleBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomGroupId(size), randomDoubleBetween(0, 100, true))) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java index 3699a87431937..b67e4cdee7e97 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunctionTests.java @@ -29,7 +29,7 @@ public class CountDistinctIntAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, Math.min(Integer.MAX_VALUE, Integer.MAX_VALUE / size)); return new SequenceIntBlockSourceOperator(LongStream.range(0, size).mapToInt(l -> between(-max, max))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunctionTests.java index f2a46e9f4c3af..678024c19d391 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -34,7 +35,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongIntBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomGroupId(size), between(0, 10000)))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java index 556f9d0ccc462..704b5c649f744 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunctionTests.java @@ -30,9 +30,9 @@ public class CountDistinctLongAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunctionTests.java index a5959471b8e15..4282adaba595e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -33,8 +34,9 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new TupleBlockSourceOperator( + blockFactory, LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomGroupId(size), randomLongBetween(0, 100_000))) ); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunctionTests.java index 54a35fcc19cb2..945c68711bb4e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -33,9 +34,10 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { if (randomBoolean()) { return new TupleBlockSourceOperator( + blockFactory, LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLong())) ); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java index eab6eb30261bd..4ae58fd8c6333 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java @@ -147,7 +147,9 @@ protected ByteSizeValue smallEnoughToCircuitBreak() { public final void testNullGroupsAndValues() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(new NullInsertingSourceOperator(simpleInput(end))); + List input = CannedSourceOperator.collectPages( + new NullInsertingSourceOperator(simpleInput(driverContext.blockFactory(), end)) + ); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -155,7 +157,7 @@ public final void testNullGroupsAndValues() { public final void testNullGroups() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(nullGroups(simpleInput(end))); + List input = CannedSourceOperator.collectPages(nullGroups(simpleInput(driverContext.blockFactory(), end))); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -184,7 +186,7 @@ protected void appendNull(ElementType elementType, Block.Builder builder, int bl public final void testNullValues() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(nullValues(simpleInput(end))); + List input = CannedSourceOperator.collectPages(nullValues(simpleInput(driverContext.blockFactory(), end))); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -192,7 +194,7 @@ public final void testNullValues() { public final void testNullValuesInitialIntermediateFinal() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(nullValues(simpleInput(end))); + List input = CannedSourceOperator.collectPages(nullValues(simpleInput(driverContext.blockFactory(), end))); List results = drive( List.of( simpleWithMode(nonBreakingBigArrays().withCircuitBreaking(), AggregatorMode.INITIAL).get(driverContext), @@ -220,7 +222,7 @@ protected void appendNull(ElementType elementType, Block.Builder builder, int bl public final void testMultivalued() { DriverContext driverContext = driverContext(); int end = between(1_000, 100_000); - List input = CannedSourceOperator.collectPages(mergeValues(simpleInput(end))); + List input = CannedSourceOperator.collectPages(mergeValues(simpleInput(driverContext.blockFactory(), end))); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -228,7 +230,9 @@ public final void testMultivalued() { public final void testMulitvaluedNullGroupsAndValues() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(new NullInsertingSourceOperator(mergeValues(simpleInput(end)))); + List input = CannedSourceOperator.collectPages( + new NullInsertingSourceOperator(mergeValues(simpleInput(driverContext.blockFactory(), end))) + ); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -236,7 +240,7 @@ public final void testMulitvaluedNullGroupsAndValues() { public final void testMulitvaluedNullGroup() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(nullGroups(mergeValues(simpleInput(end)))); + List input = CannedSourceOperator.collectPages(nullGroups(mergeValues(simpleInput(driverContext.blockFactory(), end)))); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -244,7 +248,7 @@ public final void testMulitvaluedNullGroup() { public final void testMulitvaluedNullValues() { DriverContext driverContext = driverContext(); int end = between(50, 60); - List input = CannedSourceOperator.collectPages(nullValues(mergeValues(simpleInput(end)))); + List input = CannedSourceOperator.collectPages(nullValues(mergeValues(simpleInput(driverContext.blockFactory(), end)))); List results = drive(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext), input.iterator()); assertSimpleOutput(input, results); } @@ -295,12 +299,13 @@ private void assertNullOnly(List operators) { public final void testNullSome() { DriverContext driverContext = driverContext(); - assertNullSome(List.of(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext))); + assertNullSome(driverContext, List.of(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext))); } public final void testNullSomeInitialFinal() { DriverContext driverContext = driverContext(); assertNullSome( + driverContext, List.of( simpleWithMode(nonBreakingBigArrays().withCircuitBreaking(), AggregatorMode.INITIAL).get(driverContext), simpleWithMode(nonBreakingBigArrays().withCircuitBreaking(), AggregatorMode.FINAL).get(driverContext) @@ -311,6 +316,7 @@ public final void testNullSomeInitialFinal() { public final void testNullSomeInitialIntermediateFinal() { DriverContext driverContext = driverContext(); assertNullSome( + driverContext, List.of( simpleWithMode(nonBreakingBigArrays().withCircuitBreaking(), AggregatorMode.INITIAL).get(driverContext), simpleWithMode(nonBreakingBigArrays().withCircuitBreaking(), AggregatorMode.INTERMEDIATE).get(driverContext), @@ -322,8 +328,8 @@ public final void testNullSomeInitialIntermediateFinal() { /** * Run the agg on some data where one group is always null. */ - private void assertNullSome(List operators) { - List inputData = CannedSourceOperator.collectPages(simpleInput(1000)); + private void assertNullSome(DriverContext driverContext, List operators) { + List inputData = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), 1000)); SeenGroups seenGroups = seenGroups(inputData); long nullGroup = randomFrom(seenGroups.nonNull); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunctionTests.java index b67220b4909b7..cfda483d029f6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -21,7 +22,7 @@ public class MaxDoubleAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceDoubleBlockSourceOperator(LongStream.range(0, size).mapToDouble(l -> ESTestCase.randomDouble())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunctionTests.java index 3750aec95f3a7..9a2c8bc17685d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -24,7 +25,7 @@ public class MaxDoubleGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new LongDoubleTupleBlockSourceOperator( LongStream.range(0, end).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomDouble())) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunctionTests.java index 72cfa06222b50..e76021b883120 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.operator.SequenceIntBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -20,7 +21,7 @@ public class MaxIntAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceIntBlockSourceOperator(IntStream.range(0, size).map(l -> randomInt())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunctionTests.java index 9ffee498eeba2..313e10be39855 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -33,7 +34,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongIntBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomInt()))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunctionTests.java index 4e84f2e672b97..a51aa98f7a5a8 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -20,9 +21,9 @@ public class MaxLongAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunctionTests.java index e284f2a6103d1..a1f44e128c2e1 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -33,8 +34,11 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { - return new TupleBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLong()))); + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { + return new TupleBlockSourceOperator( + blockFactory, + LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLong())) + ); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunctionTests.java index 74bda421a545e..1c14a8e7855ce 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -22,7 +23,7 @@ public class MedianAbsoluteDeviationDoubleAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { List values = Arrays.asList(1.2, 1.25, 2.0, 2.0, 4.3, 6.0, 9.0); Randomness.shuffle(values); return new SequenceDoubleBlockSourceOperator(values); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunctionTests.java index 6751486453f30..06ddb2a734f8c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -27,7 +28,7 @@ public class MedianAbsoluteDeviationDoubleGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { double[][] samples = new double[][] { { 1.2, 1.25, 2.0, 2.0, 4.3, 6.0, 9.0 }, { 0.1, 1.5, 2.0, 3.0, 4.0, 7.5, 100.0 }, diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunctionTests.java index 20506cc5c8f93..40e422b6efc26 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceIntBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -22,7 +23,7 @@ public class MedianAbsoluteDeviationIntAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { List values = Arrays.asList(12, 125, 20, 20, 43, 60, 90); Randomness.shuffle(values); return new SequenceIntBlockSourceOperator(values); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunctionTests.java index 20f62c67a16cc..2f00764f6fe51 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -27,7 +28,7 @@ public class MedianAbsoluteDeviationIntGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { int[][] samples = new int[][] { { 12, 125, 20, 20, 43, 60, 90 }, { 1, 15, 20, 30, 40, 75, 1000 }, diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunctionTests.java index d80415f83daa2..465bb5800bbb6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -22,10 +23,10 @@ public class MedianAbsoluteDeviationLongAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { List values = Arrays.asList(12L, 125L, 20L, 20L, 43L, 60L, 90L); Randomness.shuffle(values); - return new SequenceLongBlockSourceOperator(values); + return new SequenceLongBlockSourceOperator(blockFactory, values); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunctionTests.java index c3cebad8e0e0b..2c6bfc1204591 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunctionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -27,7 +28,7 @@ public class MedianAbsoluteDeviationLongGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { long[][] samples = new long[][] { { 12, 125, 20, 20, 43, 60, 90 }, { 1, 15, 20, 30, 40, 75, 1000 }, @@ -42,7 +43,7 @@ protected SourceOperator simpleInput(int end) { values.add(Tuple.tuple((long) i, v)); } } - return new TupleBlockSourceOperator(values); + return new TupleBlockSourceOperator(blockFactory, values); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunctionTests.java index 622302d549fd0..7e0b7241cf258 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -21,7 +22,7 @@ public class MinDoubleAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceDoubleBlockSourceOperator(LongStream.range(0, size).mapToDouble(l -> ESTestCase.randomDouble())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunctionTests.java index 12c63e354547a..7c4141f4a7ad1 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -23,7 +24,7 @@ public class MinDoubleGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new LongDoubleTupleBlockSourceOperator( LongStream.range(0, end).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomDouble())) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntAggregatorFunctionTests.java index 2dc0e893875ab..dc1ab1398fb90 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.operator.SequenceIntBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -20,7 +21,7 @@ public class MinIntAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceIntBlockSourceOperator(IntStream.range(0, size).map(l -> randomInt())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunctionTests.java index 4ffbe9b1396d3..55cfc2d124e5f 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -33,7 +34,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new LongIntBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomInt()))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongAggregatorFunctionTests.java index 25a420237893e..91feb141ac74b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -20,9 +21,9 @@ public class MinLongAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunctionTests.java index 311e7e41ed9ac..02dda3fe3c236 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -33,8 +34,11 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { - return new TupleBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLong()))); + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { + return new TupleBlockSourceOperator( + blockFactory, + LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLong())) + ); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunctionTests.java index 96e61d4782022..61f26cd0209b3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -41,7 +42,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceDoubleBlockSourceOperator(LongStream.range(0, size).mapToDouble(l -> ESTestCase.randomDouble())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunctionTests.java index c0d6595e088eb..9495e78ec47ca 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -42,7 +43,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new LongDoubleTupleBlockSourceOperator( LongStream.range(0, end).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomDouble())) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunctionTests.java index c34a01e608d1a..37d153f7bcae6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceIntBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -40,7 +41,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, (int) Math.min(Integer.MAX_VALUE, Long.MAX_VALUE / size)); return new SequenceIntBlockSourceOperator(LongStream.range(0, size).mapToInt(l -> between(0, max))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunctionTests.java index a018fba96e897..948e156e52c85 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -42,7 +43,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, (int) Math.min(Integer.MAX_VALUE, Long.MAX_VALUE / size)); return new LongIntBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), between(-1, max))) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunctionTests.java index cf0b18840d91e..eb32dac18ea80 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; @@ -40,9 +41,9 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, 1_000_000); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(0, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(0, max))); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunctionTests.java index 609526532b72e..6360be8595ff8 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -42,9 +43,10 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size / 5); return new TupleBlockSourceOperator( + blockFactory, LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLongBetween(-0, max))) ); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java index 767f9a2d5c25b..d3dc262419008 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.Driver; @@ -28,7 +29,7 @@ public class SumDoubleAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { return new SequenceDoubleBlockSourceOperator(LongStream.range(0, size).mapToDouble(l -> ESTestCase.randomDouble())); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunctionTests.java index 03a7269b84690..8b86d99653282 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongDoubleTupleBlockSourceOperator; @@ -23,7 +24,7 @@ public class SumDoubleGroupingAggregatorFunctionTests extends GroupingAggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new LongDoubleTupleBlockSourceOperator( LongStream.range(0, end).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomDouble())) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java index e6fccf2d46f61..736386fae3dec 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntAggregatorFunctionTests.java @@ -27,7 +27,7 @@ public class SumIntAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, (int) Math.min(Integer.MAX_VALUE, Long.MAX_VALUE / size)); return new SequenceIntBlockSourceOperator(LongStream.range(0, size).mapToInt(l -> between(-max, max))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunctionTests.java index 71666024c819d..0b8678a0e3f05 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.LongIntBlockSourceOperator; @@ -32,7 +33,7 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { int max = between(1, (int) Math.min(Integer.MAX_VALUE, Long.MAX_VALUE / size)); return new LongIntBlockSourceOperator( LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), between(-max, max))) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java index ae5aaa5b21965..e9523c5583cd4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongAggregatorFunctionTests.java @@ -27,9 +27,9 @@ public class SumLongAggregatorFunctionTests extends AggregatorFunctionTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); } @Override @@ -53,7 +53,7 @@ public void testOverflowFails() { try ( Driver d = new Driver( driverContext, - new SequenceLongBlockSourceOperator(LongStream.of(Long.MAX_VALUE - 1, 2)), + new SequenceLongBlockSourceOperator(driverContext.blockFactory(), LongStream.of(Long.MAX_VALUE - 1, 2)), List.of(simple(nonBreakingBigArrays()).get(driverContext)), new PageConsumerOperator(page -> fail("shouldn't have made it this far")), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunctionTests.java index e0dc918b515d6..827dc06a4f542 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunctionTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.SourceOperator; @@ -32,9 +33,10 @@ protected String expectedDescriptionOfAggregator() { } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size / 5); return new TupleBlockSourceOperator( + blockFactory, LongStream.range(0, size).mapToObj(l -> Tuple.tuple(randomLongBetween(0, 4), randomLongBetween(-max, max))) ); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderTests.java index de552d242afa2..2179e68c47832 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockBuilderTests.java @@ -7,47 +7,48 @@ package org.elasticsearch.compute.data; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.test.ESTestCase; +import java.util.ArrayList; import java.util.List; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; public class BlockBuilderTests extends ESTestCase { - - public void testAllNullsInt() { - for (int numEntries : List.of(1, randomIntBetween(1, 100))) { - testAllNullsImpl(IntBlock.newBlockBuilder(0), numEntries); - testAllNullsImpl(IntBlock.newBlockBuilder(100), numEntries); - testAllNullsImpl(IntBlock.newBlockBuilder(1000), numEntries); - testAllNullsImpl(IntBlock.newBlockBuilder(randomIntBetween(0, 100)), numEntries); + @ParametersFactory + public static List params() { + List params = new ArrayList<>(); + for (ElementType elementType : ElementType.values()) { + if (elementType == ElementType.UNKNOWN || elementType == ElementType.NULL || elementType == ElementType.DOC) { + continue; + } + params.add(new Object[] { elementType }); } + return params; } - public void testAllNullsLong() { - for (int numEntries : List.of(1, randomIntBetween(1, 100))) { - testAllNullsImpl(LongBlock.newBlockBuilder(0), numEntries); - testAllNullsImpl(LongBlock.newBlockBuilder(100), numEntries); - testAllNullsImpl(LongBlock.newBlockBuilder(1000), numEntries); - testAllNullsImpl(LongBlock.newBlockBuilder(randomIntBetween(0, 100)), numEntries); - } - } + private final ElementType elementType; - public void testAllNullsDouble() { - for (int numEntries : List.of(1, randomIntBetween(1, 100))) { - testAllNullsImpl(DoubleBlock.newBlockBuilder(0), numEntries); - testAllNullsImpl(DoubleBlock.newBlockBuilder(100), numEntries); - testAllNullsImpl(DoubleBlock.newBlockBuilder(1000), numEntries); - testAllNullsImpl(DoubleBlock.newBlockBuilder(randomIntBetween(0, 100)), numEntries); - } + public BlockBuilderTests(ElementType elementType) { + this.elementType = elementType; } - public void testAllNullsBytesRef() { + public void testAllNulls() { for (int numEntries : List.of(1, randomIntBetween(1, 100))) { - testAllNullsImpl(BytesRefBlock.newBlockBuilder(0), numEntries); - testAllNullsImpl(BytesRefBlock.newBlockBuilder(100), numEntries); - testAllNullsImpl(BytesRefBlock.newBlockBuilder(1000), numEntries); - testAllNullsImpl(BytesRefBlock.newBlockBuilder(randomIntBetween(0, 100)), numEntries); + testAllNullsImpl(elementType.newBlockBuilder(0), numEntries); + testAllNullsImpl(elementType.newBlockBuilder(100), numEntries); + testAllNullsImpl(elementType.newBlockBuilder(1000), numEntries); + testAllNullsImpl(elementType.newBlockBuilder(randomIntBetween(0, 100)), numEntries); } } @@ -65,4 +66,95 @@ private void testAllNullsImpl(Block.Builder builder, int numEntries) { static int randomPosition(int positionCount) { return positionCount == 1 ? 0 : randomIntBetween(0, positionCount - 1); } + + public void testCloseWithoutBuilding() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + elementType.newBlockBuilder(10, blockFactory).close(); + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testBuildSmallSingleValued() { + testBuild(between(1, 100), false, 1); + } + + public void testBuildHugeSingleValued() { + testBuild(between(1_000, 50_000), false, 1); + } + + public void testBuildSmallSingleValuedNullable() { + testBuild(between(1, 100), true, 1); + } + + public void testBuildHugeSingleValuedNullable() { + testBuild(between(1_000, 50_000), true, 1); + } + + public void testBuildSmallMultiValued() { + testBuild(between(1, 100), false, 3); + } + + public void testBuildHugeMultiValued() { + testBuild(between(1_000, 50_000), false, 3); + } + + public void testBuildSmallMultiValuedNullable() { + testBuild(between(1, 100), true, 3); + } + + public void testBuildHugeMultiValuedNullable() { + testBuild(between(1_000, 50_000), true, 3); + } + + public void testBuildSingle() { + testBuild(1, false, 1); + } + + private void testBuild(int size, boolean nullable, int maxValueCount) { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + try (Block.Builder builder = elementType.newBlockBuilder(randomBoolean() ? size : 1, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, size, nullable, 1, maxValueCount, 0, 0); + builder.copyFrom(random.block(), 0, random.block().getPositionCount()); + try (Block built = builder.build()) { + assertThat(built, equalTo(random.block())); + assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testDoubleBuild() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + try (Block.Builder builder = elementType.newBlockBuilder(10, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + builder.copyFrom(random.block(), 0, random.block().getPositionCount()); + try (Block built = builder.build()) { + assertThat(built, equalTo(random.block())); + assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + Exception e = expectThrows(IllegalStateException.class, builder::build); + assertThat(e.getMessage(), equalTo("already closed")); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testCranky() { + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new CrankyCircuitBreakerService()); + BlockFactory blockFactory = new BlockFactory(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST), bigArrays); + try { + try (Block.Builder builder = elementType.newBlockBuilder(10, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + builder.copyFrom(random.block(), 0, random.block().getPositionCount()); + try (Block built = builder.build()) { + assertThat(built, equalTo(random.block())); + } + } + // If we made it this far cranky didn't fail us! + } catch (CircuitBreakingException e) { + logger.info("cranky", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java index a524221dd50d7..24d4e27e92ad1 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -49,11 +49,29 @@ public static BlockFactory blockFactory(ByteSizeValue size) { @ParametersFactory public static List params() { - List> l = List.of(() -> { - CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); - BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); - return BlockFactory.getInstance(breaker, bigArrays); - }, BlockFactory::getGlobalInstance); + List> l = List.of(new Supplier<>() { + @Override + public BlockFactory get() { + CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + return BlockFactory.getInstance(breaker, bigArrays); + } + + @Override + public String toString() { + return "1gb"; + } + }, new Supplier<>() { + @Override + public BlockFactory get() { + return BlockFactory.getGlobalInstance(); + } + + @Override + public String toString() { + return "global"; + } + }); return l.stream().map(s -> new Object[] { s }).toList(); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockTestUtils.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockTestUtils.java index a5637128705ca..dd61c8f6478d3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockTestUtils.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockTestUtils.java @@ -77,6 +77,7 @@ public static void readInto(List> values, Page page) { for (int i = 0; i < page.getBlockCount(); i++) { readInto(values.get(i), page.getBlock(i)); } + page.releaseBlocks(); } public static void readInto(List values, Block block) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BytesRefBlockEqualityTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BytesRefBlockEqualityTests.java index 0eb9beec2e7f9..ee654497c1ec3 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BytesRefBlockEqualityTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BytesRefBlockEqualityTests.java @@ -18,7 +18,6 @@ import java.util.Arrays; import java.util.BitSet; import java.util.List; -import java.util.stream.IntStream; public class BytesRefBlockEqualityTests extends ESTestCase { @@ -332,10 +331,14 @@ public void testSimpleBlockWithSingleNull() { public void testSimpleBlockWithManyNulls() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); - IntStream.range(0, positions).forEach(i -> builder.appendNull()); - BytesRefBlock block1 = builder.build(); - BytesRefBlock block2 = builder.build(); + BytesRefBlock.Builder builder1 = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); + BytesRefBlock.Builder builder2 = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); + for (int p = 0; p < positions; p++) { + builder1.appendNull(); + builder2.appendNull(); + } + BytesRefBlock block1 = builder1.build(); + BytesRefBlock block2 = builder2.build(); assertEquals(positions, block1.getPositionCount()); assertTrue(block1.mayHaveNulls()); assertTrue(block1.isNull(0)); @@ -365,15 +368,27 @@ public void testSimpleBlockWithSingleMultiValue() { public void testSimpleBlockWithManyMultiValues() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); + BytesRefBlock.Builder builder1 = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); + BytesRefBlock.Builder builder2 = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); + BytesRefBlock.Builder builder3 = BytesRefBlock.newBlockBuilder(grow ? 0 : positions); for (int pos = 0; pos < positions; pos++) { - builder.beginPositionEntry(); + builder1.beginPositionEntry(); + builder2.beginPositionEntry(); + builder3.beginPositionEntry(); int values = randomIntBetween(1, 16); - IntStream.range(0, values).forEach(i -> builder.appendBytesRef(new BytesRef(Integer.toHexString(randomInt())))); + for (int i = 0; i < values; i++) { + BytesRef value = new BytesRef(Integer.toHexString(randomInt())); + builder1.appendBytesRef(value); + builder2.appendBytesRef(value); + builder3.appendBytesRef(value); + } + builder1.endPositionEntry(); + builder2.endPositionEntry(); + builder3.endPositionEntry(); } - BytesRefBlock block1 = builder.build(); - BytesRefBlock block2 = builder.build(); - BytesRefBlock block3 = builder.build(); + BytesRefBlock block1 = builder1.build(); + BytesRefBlock block2 = builder2.build(); + BytesRefBlock block3 = builder3.build(); assertEquals(positions, block1.getPositionCount()); assertAllEquals(List.of(block1, block2, block3)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java index d8258ab28a078..0f76e024c7436 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.compute.data; import org.elasticsearch.common.Randomness; +import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.core.Releasables; import org.elasticsearch.test.ESTestCase; @@ -98,33 +99,37 @@ public void testRandomShardSegmentDocMap() { } private void assertShardSegmentDocMap(int[][] data, int[][] expected) { - DocBlock.Builder builder = DocBlock.newBlockBuilder(data.length); - for (int r = 0; r < data.length; r++) { - builder.appendShard(data[r][0]); - builder.appendSegment(data[r][1]); - builder.appendDoc(data[r][2]); + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + try (DocBlock.Builder builder = DocBlock.newBlockBuilder(data.length, blockFactory)) { + for (int r = 0; r < data.length; r++) { + builder.appendShard(data[r][0]); + builder.appendSegment(data[r][1]); + builder.appendDoc(data[r][2]); + } + try (DocVector docVector = builder.build().asVector()) { + int[] forwards = docVector.shardSegmentDocMapForwards(); + + int[][] result = new int[docVector.getPositionCount()][]; + for (int p = 0; p < result.length; p++) { + result[p] = new int[] { + docVector.shards().getInt(forwards[p]), + docVector.segments().getInt(forwards[p]), + docVector.docs().getInt(forwards[p]) }; + } + assertThat(result, equalTo(expected)); + + int[] backwards = docVector.shardSegmentDocMapBackwards(); + for (int p = 0; p < result.length; p++) { + result[p] = new int[] { + docVector.shards().getInt(backwards[forwards[p]]), + docVector.segments().getInt(backwards[forwards[p]]), + docVector.docs().getInt(backwards[forwards[p]]) }; + } + + assertThat(result, equalTo(data)); + } } - DocVector docVector = builder.build().asVector(); - int[] forwards = docVector.shardSegmentDocMapForwards(); - - int[][] result = new int[docVector.getPositionCount()][]; - for (int p = 0; p < result.length; p++) { - result[p] = new int[] { - docVector.shards().getInt(forwards[p]), - docVector.segments().getInt(forwards[p]), - docVector.docs().getInt(forwards[p]) }; - } - assertThat(result, equalTo(expected)); - - int[] backwards = docVector.shardSegmentDocMapBackwards(); - for (int p = 0; p < result.length; p++) { - result[p] = new int[] { - docVector.shards().getInt(backwards[forwards[p]]), - docVector.segments().getInt(backwards[forwards[p]]), - docVector.docs().getInt(backwards[forwards[p]]) }; - } - - assertThat(result, equalTo(data)); + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); } public void testCannotDoubleRelease() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DoubleBlockEqualityTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DoubleBlockEqualityTests.java index 2abbcc0b989f1..7dda97f52834e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DoubleBlockEqualityTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DoubleBlockEqualityTests.java @@ -11,7 +11,6 @@ import java.util.BitSet; import java.util.List; -import java.util.stream.IntStream; public class DoubleBlockEqualityTests extends ESTestCase { @@ -224,10 +223,14 @@ public void testSimpleBlockWithSingleNull() { public void testSimpleBlockWithManyNulls() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = DoubleBlock.newBlockBuilder(grow ? 0 : positions); - IntStream.range(0, positions).forEach(i -> builder.appendNull()); - DoubleBlock block1 = builder.build(); - DoubleBlock block2 = builder.build(); + DoubleBlock.Builder builder1 = DoubleBlock.newBlockBuilder(grow ? 0 : positions); + DoubleBlock.Builder builder2 = DoubleBlock.newBlockBuilder(grow ? 0 : positions); + for (int p = 0; p < positions; p++) { + builder1.appendNull(); + builder2.appendNull(); + } + DoubleBlock block1 = builder1.build(); + DoubleBlock block2 = builder2.build(); assertEquals(positions, block1.getPositionCount()); assertTrue(block1.mayHaveNulls()); assertTrue(block1.isNull(0)); @@ -248,15 +251,27 @@ public void testSimpleBlockWithSingleMultiValue() { public void testSimpleBlockWithManyMultiValues() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = DoubleBlock.newBlockBuilder(grow ? 0 : positions); + DoubleBlock.Builder builder1 = DoubleBlock.newBlockBuilder(grow ? 0 : positions); + DoubleBlock.Builder builder2 = DoubleBlock.newBlockBuilder(grow ? 0 : positions); + DoubleBlock.Builder builder3 = DoubleBlock.newBlockBuilder(grow ? 0 : positions); for (int pos = 0; pos < positions; pos++) { - builder.beginPositionEntry(); + builder1.beginPositionEntry(); + builder2.beginPositionEntry(); + builder3.beginPositionEntry(); int values = randomIntBetween(1, 16); - IntStream.range(0, values).forEach(i -> builder.appendDouble(randomDouble())); + for (int i = 0; i < values; i++) { + double value = randomDouble(); + builder1.appendDouble(value); + builder2.appendDouble(value); + builder3.appendDouble(value); + } + builder1.endPositionEntry(); + builder2.endPositionEntry(); + builder3.endPositionEntry(); } - DoubleBlock block1 = builder.build(); - DoubleBlock block2 = builder.build(); - DoubleBlock block3 = builder.build(); + DoubleBlock block1 = builder1.build(); + DoubleBlock block2 = builder2.build(); + DoubleBlock block3 = builder3.build(); assertEquals(positions, block1.getPositionCount()); assertAllEquals(List.of(block1, block2, block3)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/IntBlockEqualityTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/IntBlockEqualityTests.java index c4e19106d4368..40c84324f13d2 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/IntBlockEqualityTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/IntBlockEqualityTests.java @@ -11,7 +11,6 @@ import java.util.BitSet; import java.util.List; -import java.util.stream.IntStream; public class IntBlockEqualityTests extends ESTestCase { @@ -185,10 +184,14 @@ public void testSimpleBlockWithSingleNull() { public void testSimpleBlockWithManyNulls() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = IntBlock.newBlockBuilder(grow ? 0 : positions); - IntStream.range(0, positions).forEach(i -> builder.appendNull()); - IntBlock block1 = builder.build(); - IntBlock block2 = builder.build(); + IntBlock.Builder builder1 = IntBlock.newBlockBuilder(grow ? 0 : positions); + IntBlock.Builder builder2 = IntBlock.newBlockBuilder(grow ? 0 : positions); + for (int p = 0; p < positions; p++) { + builder1.appendNull(); + builder2.appendNull(); + } + IntBlock block1 = builder1.build(); + IntBlock block2 = builder2.build(); assertEquals(positions, block1.getPositionCount()); assertTrue(block1.mayHaveNulls()); assertTrue(block1.isNull(0)); @@ -210,15 +213,27 @@ public void testSimpleBlockWithSingleMultiValue() { public void testSimpleBlockWithManyMultiValues() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = IntBlock.newBlockBuilder(grow ? 0 : positions); + IntBlock.Builder builder1 = IntBlock.newBlockBuilder(grow ? 0 : positions); + IntBlock.Builder builder2 = IntBlock.newBlockBuilder(grow ? 0 : positions); + IntBlock.Builder builder3 = IntBlock.newBlockBuilder(grow ? 0 : positions); for (int pos = 0; pos < positions; pos++) { - builder.beginPositionEntry(); + builder1.beginPositionEntry(); + builder2.beginPositionEntry(); + builder3.beginPositionEntry(); int values = randomIntBetween(1, 16); - IntStream.range(0, values).forEach(i -> builder.appendInt(randomInt())); + for (int i = 0; i < values; i++) { + int value = randomInt(); + builder1.appendInt(value); + builder2.appendInt(value); + builder3.appendInt(value); + } + builder1.endPositionEntry(); + builder2.endPositionEntry(); + builder3.endPositionEntry(); } - IntBlock block1 = builder.build(); - IntBlock block2 = builder.build(); - IntBlock block3 = builder.build(); + IntBlock block1 = builder1.build(); + IntBlock block2 = builder2.build(); + IntBlock block3 = builder3.build(); assertEquals(positions, block1.getPositionCount()); assertAllEquals(List.of(block1, block2, block3)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/LongBlockEqualityTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/LongBlockEqualityTests.java index 3d08b2a96d635..a24b4a4dd6fa6 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/LongBlockEqualityTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/LongBlockEqualityTests.java @@ -11,7 +11,6 @@ import java.util.BitSet; import java.util.List; -import java.util.stream.IntStream; public class LongBlockEqualityTests extends ESTestCase { @@ -191,10 +190,14 @@ public void testSimpleBlockWithSingleNull() { public void testSimpleBlockWithManyNulls() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = LongBlock.newBlockBuilder(grow ? 0 : positions); - IntStream.range(0, positions).forEach(i -> builder.appendNull()); - LongBlock block1 = builder.build(); - LongBlock block2 = builder.build(); + LongBlock.Builder builder1 = LongBlock.newBlockBuilder(grow ? 0 : positions); + LongBlock.Builder builder2 = LongBlock.newBlockBuilder(grow ? 0 : positions); + for (int p = 0; p < positions; p++) { + builder1.appendNull(); + builder2.appendNull(); + } + LongBlock block1 = builder1.build(); + LongBlock block2 = builder2.build(); assertEquals(positions, block1.getPositionCount()); assertTrue(block1.mayHaveNulls()); assertTrue(block1.isNull(0)); @@ -216,15 +219,27 @@ public void testSimpleBlockWithSingleMultiValue() { public void testSimpleBlockWithManyMultiValues() { int positions = randomIntBetween(1, 256); boolean grow = randomBoolean(); - var builder = LongBlock.newBlockBuilder(grow ? 0 : positions); + LongBlock.Builder builder1 = LongBlock.newBlockBuilder(grow ? 0 : positions); + LongBlock.Builder builder2 = LongBlock.newBlockBuilder(grow ? 0 : positions); + LongBlock.Builder builder3 = LongBlock.newBlockBuilder(grow ? 0 : positions); for (int pos = 0; pos < positions; pos++) { - builder.beginPositionEntry(); - int values = randomIntBetween(1, 16); - IntStream.range(0, values).forEach(i -> builder.appendLong(randomLong())); + builder1.beginPositionEntry(); + builder2.beginPositionEntry(); + builder3.beginPositionEntry(); + int valueCount = randomIntBetween(1, 16); + for (int i = 0; i < valueCount; i++) { + long value = randomLong(); + builder1.appendLong(value); + builder2.appendLong(value); + builder3.appendLong(value); + } + builder1.endPositionEntry(); + builder2.endPositionEntry(); + builder3.endPositionEntry(); } - LongBlock block1 = builder.build(); - LongBlock block2 = builder.build(); - LongBlock block3 = builder.build(); + LongBlock block1 = builder1.build(); + LongBlock block2 = builder2.build(); + LongBlock block3 = builder3.build(); assertEquals(positions, block1.getPositionCount()); assertAllEquals(List.of(block1, block2, block3)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/TestBlockBuilder.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/TestBlockBuilder.java index 4684da93a661a..d9377a490368d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/TestBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/TestBlockBuilder.java @@ -139,6 +139,11 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { public IntBlock build() { return builder.build(); } + + @Override + public void close() { + builder.close(); + } } private static class TestLongBlockBuilder extends TestBlockBuilder { @@ -195,6 +200,11 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { public LongBlock build() { return builder.build(); } + + @Override + public void close() { + builder.close(); + } } private static class TestDoubleBlockBuilder extends TestBlockBuilder { @@ -251,6 +261,11 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { public DoubleBlock build() { return builder.build(); } + + @Override + public void close() { + builder.close(); + } } private static class TestBytesRefBlockBuilder extends TestBlockBuilder { @@ -307,6 +322,11 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { public BytesRefBlock build() { return builder.build(); } + + @Override + public void close() { + builder.close(); + } } private static class TestBooleanBlockBuilder extends TestBlockBuilder { @@ -366,5 +386,10 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { public BooleanBlock build() { return builder.build(); } + + @Override + public void close() { + builder.close(); + } } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorBuilderTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorBuilderTests.java new file mode 100644 index 0000000000000..656d79070f217 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorBuilderTests.java @@ -0,0 +1,153 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.indices.CrankyCircuitBreakerService; +import org.elasticsearch.test.ESTestCase; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + +public class VectorBuilderTests extends ESTestCase { + @ParametersFactory + public static List params() { + List params = new ArrayList<>(); + for (ElementType elementType : ElementType.values()) { + if (elementType == ElementType.UNKNOWN || elementType == ElementType.NULL || elementType == ElementType.DOC) { + continue; + } + params.add(new Object[] { elementType }); + } + return params; + } + + private final ElementType elementType; + + public VectorBuilderTests(ElementType elementType) { + this.elementType = elementType; + } + + public void testCloseWithoutBuilding() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + vectorBuilder(10, blockFactory).close(); + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testBuildSmall() { + testBuild(between(1, 100)); + } + + public void testBuildHuge() { + testBuild(between(1_000, 50_000)); + } + + public void testBuildSingle() { + testBuild(1); + } + + private void testBuild(int size) { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + try (Vector.Builder builder = vectorBuilder(randomBoolean() ? size : 1, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, size, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testDoubleBuild() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + try (Vector.Builder builder = vectorBuilder(10, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + Exception e = expectThrows(IllegalStateException.class, builder::build); + assertThat(e.getMessage(), equalTo("already closed")); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + public void testCranky() { + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new CrankyCircuitBreakerService()); + BlockFactory blockFactory = new BlockFactory(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST), bigArrays); + try { + try (Vector.Builder builder = vectorBuilder(10, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + } + } + // If we made it this far cranky didn't fail us! + } catch (CircuitBreakingException e) { + logger.info("cranky", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + + private Vector.Builder vectorBuilder(int estimatedSize, BlockFactory blockFactory) { + return switch (elementType) { + case NULL, DOC, UNKNOWN -> throw new UnsupportedOperationException(); + case BOOLEAN -> BooleanVector.newVectorBuilder(estimatedSize, blockFactory); + case BYTES_REF -> BytesRefVector.newVectorBuilder(estimatedSize, blockFactory); + case DOUBLE -> DoubleVector.newVectorBuilder(estimatedSize, blockFactory); + case INT -> IntVector.newVectorBuilder(estimatedSize, blockFactory); + case LONG -> LongVector.newVectorBuilder(estimatedSize, blockFactory); + }; + } + + private void fill(Vector.Builder builder, Vector from) { + switch (elementType) { + case NULL, DOC, UNKNOWN -> throw new UnsupportedOperationException(); + case BOOLEAN -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((BooleanVector.Builder) builder).appendBoolean(((BooleanVector) from).getBoolean(p)); + } + } + case BYTES_REF -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((BytesRefVector.Builder) builder).appendBytesRef(((BytesRefVector) from).getBytesRef(p, new BytesRef())); + } + } + case DOUBLE -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((DoubleVector.Builder) builder).appendDouble(((DoubleVector) from).getDouble(p)); + } + } + case INT -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((IntVector.Builder) builder).appendInt(((IntVector) from).getInt(p)); + } + } + case LONG -> { + for (int p = 0; p < from.getPositionCount(); p++) { + ((LongVector.Builder) builder).appendLong(((LongVector) from).getLong(p)); + } + } + } + } +} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java index 9fa9f7e32c654..df67ee2e7822a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/VectorFixedBuilderTests.java @@ -45,6 +45,12 @@ public VectorFixedBuilderTests(ElementType elementType) { this.elementType = elementType; } + public void testCloseWithoutBuilding() { + BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); + vectorBuilder(10, blockFactory).close(); + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } + public void testBuildSmall() { testBuild(between(1, 100)); } @@ -59,25 +65,32 @@ public void testBuildSingle() { private void testBuild(int size) { BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); - Vector.Builder builder = vectorBuilder(size, blockFactory); - BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, size, false, 1, 1, 0, 0); - fill(builder, random.block().asVector()); - try (Vector built = builder.build()) { - assertThat(built, equalTo(random.block().asVector())); - assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + try (Vector.Builder builder = vectorBuilder(size, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, size, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + assertThat(blockFactory.breaker().getUsed(), equalTo(built.ramBytesUsed())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); } public void testDoubleBuild() { BlockFactory blockFactory = BlockFactoryTests.blockFactory(ByteSizeValue.ofGb(1)); - Vector.Builder builder = vectorBuilder(10, blockFactory); - BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); - fill(builder, random.block().asVector()); - try (Vector built = builder.build()) { - assertThat(built, equalTo(random.block().asVector())); + try (Vector.Builder builder = vectorBuilder(10, blockFactory)) { + BasicBlockTests.RandomBlock random = BasicBlockTests.randomBlock(elementType, 10, false, 1, 1, 0, 0); + fill(builder, random.block().asVector()); + try (Vector built = builder.build()) { + assertThat(built, equalTo(random.block().asVector())); + } + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + Exception e = expectThrows(IllegalStateException.class, builder::build); + assertThat(e.getMessage(), equalTo("already closed")); } - Exception e = expectThrows(IllegalStateException.class, builder::build); - assertThat(e.getMessage(), equalTo("already closed")); + assertThat(blockFactory.breaker().getUsed(), equalTo(0L)); + } public void testCranky() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java index 64edcaa43d89b..4776b40e12115 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.data.BytesRefBlock; @@ -115,7 +116,7 @@ static Operator.OperatorFactory factory(IndexReader reader, ValuesSourceType vsT } @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { // The test wants more than one segment. We shoot for about 10. int commitEvery = Math.max(1, size / 10); try ( @@ -198,21 +199,35 @@ protected ByteSizeValue smallEnoughToCircuitBreak() { } public void testLoadAll() { - loadSimpleAndAssert(CannedSourceOperator.collectPages(simpleInput(between(1_000, 100 * 1024)))); + DriverContext driverContext = driverContext(); + loadSimpleAndAssert( + driverContext, + CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100 * 1024))) + ); } public void testLoadAllInOnePage() { + DriverContext driverContext = driverContext(); loadSimpleAndAssert( - List.of(CannedSourceOperator.mergePages(CannedSourceOperator.collectPages(simpleInput(between(1_000, 100 * 1024))))) + driverContext, + List.of( + CannedSourceOperator.mergePages( + CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100 * 1024))) + ) + ) ); } public void testEmpty() { - loadSimpleAndAssert(CannedSourceOperator.collectPages(simpleInput(0))); + DriverContext driverContext = driverContext(); + loadSimpleAndAssert(driverContext, CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), 0))); } public void testLoadAllInOnePageShuffled() { - Page source = CannedSourceOperator.mergePages(CannedSourceOperator.collectPages(simpleInput(between(1_000, 100 * 1024)))); + DriverContext driverContext = driverContext(); + Page source = CannedSourceOperator.mergePages( + CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100 * 1024))) + ); List shuffleList = new ArrayList<>(); IntStream.range(0, source.getPositionCount()).forEach(i -> shuffleList.add(i)); Randomness.shuffle(shuffleList); @@ -222,11 +237,10 @@ public void testLoadAllInOnePageShuffled() { shuffledBlocks[b] = source.getBlock(b).filter(shuffleArray); } source = new Page(shuffledBlocks); - loadSimpleAndAssert(List.of(source)); + loadSimpleAndAssert(driverContext, List.of(source)); } - private void loadSimpleAndAssert(List input) { - DriverContext driverContext = driverContext(); + private void loadSimpleAndAssert(DriverContext driverContext, List input) { List results = new ArrayList<>(); List operators = List.of( factory( diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AggregationOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AggregationOperatorTests.java index 9eaa1e333f66e..784d5134e9608 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AggregationOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AggregationOperatorTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.compute.aggregation.SumLongAggregatorFunctionSupplier; import org.elasticsearch.compute.aggregation.SumLongAggregatorFunctionTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import java.util.List; @@ -28,9 +29,9 @@ public class AggregationOperatorTests extends ForkingOperatorTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size).map(l -> randomLongBetween(-max, max))); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java index 8f995d9a31bc3..14d83ce252e5f 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java @@ -95,7 +95,7 @@ protected final BigArrays nonBreakingBigArrays() { /** * A {@link DriverContext} with a nonBreakingBigArrays. */ - protected DriverContext driverContext() { + protected DriverContext driverContext() { // TODO make this final and return a breaking block factory return new DriverContext(nonBreakingBigArrays(), BlockFactory.getNonBreakingInstance()); } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java index d5b07a713b8b4..57ea313b88dab 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java @@ -53,6 +53,25 @@ public static Page mergePages(List pages) { return new Page(blocks); } + /** + * Make a deep copy of some pages. Useful so that when the originals are + * released the copies are still live. + */ + public static List deepCopyOf(List pages) { + List out = new ArrayList<>(pages.size()); + for (Page p : pages) { + Block[] blocks = new Block[p.getBlockCount()]; + for (int b = 0; b < blocks.length; b++) { + Block orig = p.getBlock(b); + Block.Builder builder = orig.elementType().newBlockBuilder(p.getPositionCount()); + builder.copyFrom(orig, 0, p.getPositionCount()); + blocks[b] = builder.build(); + } + out.add(new Page(blocks)); + } + return out; + } + private final Iterator page; public CannedSourceOperator(Iterator page) { @@ -77,5 +96,9 @@ public Page getOutput() { } @Override - public void close() {} + public void close() { + while (page.hasNext()) { + page.next().releaseBlocks(); + } + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java index 7825e035df0db..f06a2780b7446 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; @@ -24,7 +25,7 @@ public class ColumnExtractOperatorTests extends OperatorTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { List input = LongStream.range(0, end) .mapToObj(l -> new BytesRef("word1_" + l + " word2_" + l + " word3_" + l)) .collect(Collectors.toList()); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java index 156f37d8d8e7a..91e18214abee2 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Page; @@ -21,8 +22,8 @@ public class EvalOperatorTests extends OperatorTestCase { @Override - protected SourceOperator simpleInput(int end) { - return new TupleBlockSourceOperator(LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { + return new TupleBlockSourceOperator(blockFactory, LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); } record Addition(int lhs, int rhs) implements EvalOperator.ExpressionEvaluator { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java index b26fe0c33fe1c..9c29471473203 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; @@ -23,8 +24,8 @@ public class FilterOperatorTests extends OperatorTestCase { @Override - protected SourceOperator simpleInput(int end) { - return new TupleBlockSourceOperator(LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { + return new TupleBlockSourceOperator(blockFactory, LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); } record SameLastDigit(int lhs, int rhs) implements EvalOperator.ExpressionEvaluator { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java index 9d1084fcc4cf3..d01a5b17ac788 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java @@ -56,7 +56,7 @@ protected final Operator.OperatorFactory simple(BigArrays bigArrays) { public final void testInitialFinal() { BigArrays bigArrays = nonBreakingBigArrays(); DriverContext driverContext = driverContext(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100_000))); List results = new ArrayList<>(); try ( @@ -80,7 +80,7 @@ public final void testInitialFinal() { public final void testManyInitialFinal() { BigArrays bigArrays = nonBreakingBigArrays(); DriverContext driverContext = driverContext(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100_000))); List partials = oneDriverPerPage(input, () -> List.of(simpleWithMode(bigArrays, AggregatorMode.INITIAL).get(driverContext))); List results = new ArrayList<>(); try ( @@ -101,7 +101,7 @@ public final void testManyInitialFinal() { public final void testInitialIntermediateFinal() { BigArrays bigArrays = nonBreakingBigArrays(); DriverContext driverContext = driverContext(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100_000))); List results = new ArrayList<>(); try ( @@ -127,7 +127,7 @@ public final void testInitialIntermediateFinal() { public final void testManyInitialManyPartialFinal() { BigArrays bigArrays = nonBreakingBigArrays(); DriverContext driverContext = driverContext(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext.blockFactory(), between(1_000, 100_000))); List partials = oneDriverPerPage(input, () -> List.of(simpleWithMode(bigArrays, AggregatorMode.INITIAL).get(driverContext))); Collections.shuffle(partials, random()); @@ -156,7 +156,7 @@ public final void testManyInitialManyPartialFinal() { // to move the data through the pipeline. public final void testManyInitialManyPartialFinalRunner() { BigArrays bigArrays = nonBreakingBigArrays(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext().blockFactory(), between(1_000, 100_000))); List results = new ArrayList<>(); List drivers = createDriversForInput(bigArrays, input, results, false /* no throwing ops */); @@ -178,7 +178,7 @@ protected void start(Driver driver, ActionListener listener) { // runner behaves correctly and also releases all resources (bigArrays) appropriately. public final void testManyInitialManyPartialFinalRunnerThrowing() { BigArrays bigArrays = nonBreakingBigArrays(); - List input = CannedSourceOperator.collectPages(simpleInput(between(1_000, 100_000))); + List input = CannedSourceOperator.collectPages(simpleInput(driverContext().blockFactory(), between(1_000, 100_000))); List results = new ArrayList<>(); List drivers = createDriversForInput(bigArrays, input, results, true /* one throwing op */); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java index 954a1f179f259..1afa5d3c02330 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.compute.aggregation.SumLongAggregatorFunctionSupplier; import org.elasticsearch.compute.aggregation.SumLongGroupingAggregatorFunctionTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; @@ -31,9 +32,12 @@ public class HashAggregationOperatorTests extends ForkingOperatorTestCase { @Override - protected SourceOperator simpleInput(int size) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); - return new TupleBlockSourceOperator(LongStream.range(0, size).mapToObj(l -> Tuple.tuple(l % 5, randomLongBetween(-max, max)))); + return new TupleBlockSourceOperator( + blockFactory, + LongStream.range(0, size).mapToObj(l -> Tuple.tuple(l % 5, randomLongBetween(-max, max))) + ); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java index 228fdf262cf62..bbbfd44014ffc 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; import java.util.List; @@ -24,8 +25,8 @@ protected Operator.OperatorFactory simple(BigArrays bigArrays) { } @Override - protected SourceOperator simpleInput(int size) { - return new SequenceLongBlockSourceOperator(LongStream.range(0, size)); + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { + return new SequenceLongBlockSourceOperator(blockFactory, LongStream.range(0, size)); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java index 80ac57ed539e7..21ca59e0f45a4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BasicBlockTests; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; @@ -24,7 +25,7 @@ public class MvExpandOperatorTests extends OperatorTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new AbstractBlockSourceOperator(8 * 1024) { private int idx; diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java index 3b2fac5271aa6..aee600b079b81 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.Randomness; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArray; @@ -21,6 +22,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.threadpool.FixedExecutorBuilder; @@ -36,6 +38,7 @@ import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.in; /** * Base tests for {@link Operator}s that are not {@link SourceOperator} or {@link SinkOperator}. @@ -44,7 +47,7 @@ public abstract class OperatorTestCase extends AnyOperatorTestCase { /** * Valid input to be sent to {@link #simple}; */ - protected abstract SourceOperator simpleInput(int size); + protected abstract SourceOperator simpleInput(BlockFactory blockFactory, int size); /** * Assert that output from {@link #simple} is correct for the @@ -80,15 +83,27 @@ public final void testSimpleLargeInput() { * in a sane way. */ public final void testSimpleCircuitBreaking() { - BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, smallEnoughToCircuitBreak()); + /* + * We build two CircuitBreakers - one for the input blocks and one for the operation itself. + * The input blocks don't count against the memory usage for the limited operator that we + * build. + */ + DriverContext inputFactoryContext = driverContext(); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, smallEnoughToCircuitBreak()) + .withCircuitBreaking(); + List input = CannedSourceOperator.collectPages(simpleInput(inputFactoryContext.blockFactory(), between(1_000, 10_000))); CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); Exception e = expectThrows( CircuitBreakingException.class, - () -> assertSimple(new DriverContext(bigArrays, blockFactory), between(1_000, 10_000)) + () -> drive(simple(bigArrays).get(new DriverContext(bigArrays, blockFactory)), input.iterator()) ); assertThat(e.getMessage(), equalTo(MockBigArrays.ERROR_MESSAGE)); assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); + + // Note the lack of try/finally here - we're asserting that when the driver throws an exception we clear the breakers. + assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); + assertThat(inputFactoryContext.breaker().getUsed(), equalTo(0L)); } /** @@ -98,15 +113,24 @@ public final void testSimpleCircuitBreaking() { * in ctors. */ public final void testSimpleWithCranky() { - CrankyCircuitBreakerService breaker = new CrankyCircuitBreakerService(); - BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, breaker).withCircuitBreaking(); - BlockFactory blockFactory = BlockFactory.getInstance(breaker.getBreaker("request"), bigArrays); + DriverContext inputFactoryContext = driverContext(); + List input = CannedSourceOperator.collectPages(simpleInput(inputFactoryContext.blockFactory(), between(1_000, 10_000))); + + CrankyCircuitBreakerService cranky = new CrankyCircuitBreakerService(); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, cranky).withCircuitBreaking(); + BlockFactory blockFactory = BlockFactory.getInstance(cranky.getBreaker(CircuitBreaker.REQUEST), bigArrays); try { - assertSimple(new DriverContext(bigArrays, blockFactory), between(1_000, 10_000)); + List result = drive(simple(bigArrays).get(new DriverContext(bigArrays, blockFactory)), input.iterator()); + Releasables.close(() -> Iterators.map(result.iterator(), p -> p::releaseBlocks)); // Either we get lucky and cranky doesn't throw and the test completes or we don't and it throws } catch (CircuitBreakingException e) { + logger.info("broken", e); assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); } + + // Note the lack of try/finally here - we're asserting that when the driver throws an exception we clear the breakers. + assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); + assertThat(inputFactoryContext.breaker().getUsed(), equalTo(0L)); } /** @@ -139,10 +163,12 @@ protected final List oneDriverPerPageList(Iterator> source, Sup } private void assertSimple(DriverContext context, int size) { - List input = CannedSourceOperator.collectPages(simpleInput(size)); + List input = CannedSourceOperator.collectPages(simpleInput(context.blockFactory(), size)); + // Clone the input so that the operator can close it, then, later, we can read it again to build the assertion. + List inputClone = CannedSourceOperator.deepCopyOf(input); BigArrays bigArrays = context.bigArrays().withCircuitBreaking(); List results = drive(simple(bigArrays).get(context), input.iterator()); - assertSimpleOutput(input, results); + assertSimpleOutput(inputClone, results); results.forEach(Page::releaseBlocks); assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); } @@ -180,7 +206,11 @@ public static void runDriver(List drivers) { "dummy-session", new DriverContext(BigArrays.NON_RECYCLING_INSTANCE, BlockFactory.getNonBreakingInstance()), () -> "dummy-driver", - new SequenceLongBlockSourceOperator(LongStream.range(0, between(1, 100)), between(1, 100)), + new SequenceLongBlockSourceOperator( + BlockFactory.getNonBreakingInstance(), + LongStream.range(0, between(1, 100)), + between(1, 100) + ), List.of(), new PageConsumerOperator(page -> {}), Driver.DEFAULT_STATUS_INTERVAL, diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index baa7842bdc1f9..6ec0183bbf224 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -90,7 +90,7 @@ private List randomProjection(int size) { } @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { return new TupleBlockSourceOperator(blockFactory, LongStream.range(0, end).mapToObj(l -> Tuple.tuple(l, end - l))); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java index 0aa78f3ad0ab3..f7c3ee825d695 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/SequenceLongBlockSourceOperator.java @@ -21,23 +21,27 @@ public class SequenceLongBlockSourceOperator extends AbstractBlockSourceOperator static final int DEFAULT_MAX_PAGE_POSITIONS = 8 * 1024; + private final BlockFactory blockFactory; + private final long[] values; - public SequenceLongBlockSourceOperator(LongStream values) { - this(values, DEFAULT_MAX_PAGE_POSITIONS); + public SequenceLongBlockSourceOperator(BlockFactory blockFactory, LongStream values) { + this(blockFactory, values, DEFAULT_MAX_PAGE_POSITIONS); } - public SequenceLongBlockSourceOperator(LongStream values, int maxPagePositions) { + public SequenceLongBlockSourceOperator(BlockFactory blockFactory, LongStream values, int maxPagePositions) { super(maxPagePositions); + this.blockFactory = blockFactory; this.values = values.toArray(); } - public SequenceLongBlockSourceOperator(List values) { - this(values, DEFAULT_MAX_PAGE_POSITIONS); + public SequenceLongBlockSourceOperator(BlockFactory blockFactory, List values) { + this(blockFactory, values, DEFAULT_MAX_PAGE_POSITIONS); } - public SequenceLongBlockSourceOperator(List values, int maxPagePositions) { + public SequenceLongBlockSourceOperator(BlockFactory blockFactory, List values, int maxPagePositions) { super(maxPagePositions); + this.blockFactory = blockFactory; this.values = values.stream().mapToLong(Long::longValue).toArray(); } @@ -48,7 +52,7 @@ protected Page createPage(int positionOffset, int length) { array[i] = values[positionOffset + i]; } currentPosition += length; - return new Page(BlockFactory.getNonBreakingInstance().newLongArrayVector(array, array.length).asBlock()); // TODO: just for compile + return new Page(blockFactory.newLongArrayVector(array, array.length).asBlock()); // TODO: just for compile } protected int remaining() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java index f3c67f18589fa..1e72ecb0c3ee4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.Page; @@ -25,7 +26,7 @@ public class StringExtractOperatorTests extends OperatorTestCase { @Override - protected SourceOperator simpleInput(int end) { + protected SourceOperator simpleInput(BlockFactory blockFactory, int end) { List input = LongStream.range(0, end) .mapToObj(l -> new BytesRef("word1_" + l + " word2_" + l + " word3_" + l)) .collect(Collectors.toList()); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java index 78cff5897c917..9b87dbe01224a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TupleBlockSourceOperator.java @@ -26,10 +26,6 @@ public class TupleBlockSourceOperator extends AbstractBlockSourceOperator { private final List> values; - public TupleBlockSourceOperator(Stream> values) { - this(BlockFactory.getNonBreakingInstance(), values, DEFAULT_MAX_PAGE_POSITIONS); - } - public TupleBlockSourceOperator(BlockFactory blockFactory, Stream> values) { this(blockFactory, values, DEFAULT_MAX_PAGE_POSITIONS); } @@ -40,14 +36,14 @@ public TupleBlockSourceOperator(BlockFactory blockFactory, Stream> values) { - this(values, DEFAULT_MAX_PAGE_POSITIONS); + public TupleBlockSourceOperator(BlockFactory blockFactory, List> values) { + this(blockFactory, values, DEFAULT_MAX_PAGE_POSITIONS); } - public TupleBlockSourceOperator(List> values, int maxPagePositions) { + public TupleBlockSourceOperator(BlockFactory blockFactory, List> values, int maxPagePositions) { super(maxPagePositions); + this.blockFactory = blockFactory; this.values = values; - blockFactory = BlockFactory.getNonBreakingInstance(); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/ExtractorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/ExtractorTests.java index d79fde19f5487..9c4358e5d9ee0 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/ExtractorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/ExtractorTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BlockTestUtils; import org.elasticsearch.compute.data.BlockUtils; import org.elasticsearch.compute.data.DocVector; @@ -142,7 +143,13 @@ public void testNotInKey() { ValueExtractor.extractorFor(testCase.type, testCase.encoder.toUnsortable(), false, value).writeValue(valuesBuilder, 0); assertThat(valuesBuilder.length(), greaterThan(0)); - ResultBuilder result = ResultBuilder.resultBuilderFor(testCase.type, testCase.encoder.toUnsortable(), false, 1); + ResultBuilder result = ResultBuilder.resultBuilderFor( + BlockFactory.getNonBreakingInstance(), + testCase.type, + testCase.encoder.toUnsortable(), + false, + 1 + ); BytesRef values = valuesBuilder.bytesRefView(); result.decodeValue(values); assertThat(values.length, equalTo(0)); @@ -163,7 +170,13 @@ public void testInKey() { ValueExtractor.extractorFor(testCase.type, testCase.encoder.toUnsortable(), true, value).writeValue(valuesBuilder, 0); assertThat(valuesBuilder.length(), greaterThan(0)); - ResultBuilder result = ResultBuilder.resultBuilderFor(testCase.type, testCase.encoder.toUnsortable(), true, 1); + ResultBuilder result = ResultBuilder.resultBuilderFor( + BlockFactory.getNonBreakingInstance(), + testCase.type, + testCase.encoder.toUnsortable(), + true, + 1 + ); BytesRef keys = keysBuilder.bytesRefView(); if (testCase.type == ElementType.NULL) { assertThat(keys.length, equalTo(1)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java index 7491ffde6766e..95f6613d3c0a4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; @@ -37,16 +38,21 @@ import org.elasticsearch.compute.operator.TupleBlockSourceOperator; import org.elasticsearch.core.Tuple; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.ListMatcher; import org.elasticsearch.xpack.versionfield.Version; +import org.junit.After; +import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -143,8 +149,12 @@ protected String expectedToStringOfSimple() { } @Override - protected SourceOperator simpleInput(int size) { - return new SequenceLongBlockSourceOperator(LongStream.range(0, size).map(l -> ESTestCase.randomLong()), between(1, size * 2)); + protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { + return new SequenceLongBlockSourceOperator( + blockFactory, + LongStream.range(0, size).map(l -> ESTestCase.randomLong()), + between(1, size * 2) + ); } @Override @@ -180,26 +190,48 @@ protected ByteSizeValue smallEnoughToCircuitBreak() { } public void testRamBytesUsed() { + RamUsageTester.Accumulator acc = new RamUsageTester.Accumulator() { + @Override + public long accumulateObject(Object o, long shallowSize, Map fieldValues, Collection queue) { + if (o instanceof ElementType) { + return 0; // shared + } + if (o instanceof TopNEncoder) { + return 0; // shared + } + if (o instanceof CircuitBreaker) { + return 0; // shared + } + if (o instanceof BlockFactory) { + return 0; // shard + } + return super.accumulateObject(o, shallowSize, fieldValues, queue); + } + }; int topCount = 10_000; // We under-count by a few bytes because of the lists. In that end that's fine, but we need to account for it here. - long underCount = 100; - TopNOperator op = new TopNOperator.TopNOperatorFactory( - topCount, - List.of(LONG), - List.of(DEFAULT_UNSORTABLE), - List.of(new TopNOperator.SortOrder(0, true, false)), - pageSize - ).get(driverContext()); - long actualEmpty = RamUsageTester.ramUsed(op) - RamUsageTester.ramUsed(LONG) - RamUsageTester.ramUsed(DEFAULT_UNSORTABLE) - - RamUsageTester.ramUsed(op.breaker()); - assertThat(op.ramBytesUsed(), both(greaterThan(actualEmpty - underCount)).and(lessThan(actualEmpty))); - // But when we fill it then we're quite close - for (Page p : CannedSourceOperator.collectPages(simpleInput(topCount))) { - op.addInput(p); + long underCount = 200; + DriverContext context = driverContext(); + try ( + TopNOperator op = new TopNOperator.TopNOperatorFactory( + topCount, + List.of(LONG), + List.of(DEFAULT_UNSORTABLE), + List.of(new TopNOperator.SortOrder(0, true, false)), + pageSize + ).get(context) + ) { + long actualEmpty = RamUsageTester.ramUsed(op, acc); + assertThat(op.ramBytesUsed(), both(greaterThan(actualEmpty - underCount)).and(lessThan(actualEmpty))); + // But when we fill it then we're quite close + for (Page p : CannedSourceOperator.collectPages(simpleInput(context.blockFactory(), topCount))) { + op.addInput(p); + } + long actualFull = RamUsageTester.ramUsed(op, acc); + assertThat(op.ramBytesUsed(), both(greaterThan(actualFull - underCount)).and(lessThan(actualFull))); + + // TODO empty it again and check. } - long actualFull = RamUsageTester.ramUsed(op) - RamUsageTester.ramUsed(LONG) - RamUsageTester.ramUsed(DEFAULT_UNSORTABLE) - - RamUsageTester.ramUsed(op.breaker()); - assertThat(op.ramBytesUsed(), both(greaterThan(actualFull - underCount)).and(lessThan(actualFull))); } public void testRandomTopN() { @@ -471,6 +503,7 @@ public void testCollectAllValues() { new CannedSourceOperator(List.of(new Page(blocks.toArray(Block[]::new))).iterator()), List.of( new TopNOperator( + blockFactory, nonBreakingBigArrays().breakerService().getBreaker("request"), topCount, elementTypes, @@ -559,6 +592,7 @@ public void testCollectAllValues_RandomMultiValues() { new CannedSourceOperator(List.of(new Page(blocks.toArray(Block[]::new))).iterator()), List.of( new TopNOperator( + blockFactory, nonBreakingBigArrays().breakerService().getBreaker("request"), topCount, elementTypes, @@ -590,9 +624,10 @@ private List> topNTwoColumns( try ( Driver driver = new Driver( driverContext, - new TupleBlockSourceOperator(inputValues, randomIntBetween(1, 1000)), + new TupleBlockSourceOperator(driverContext.blockFactory(), inputValues, randomIntBetween(1, 1000)), List.of( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), limit, elementTypes, @@ -607,6 +642,7 @@ private List> topNTwoColumns( for (int i = 0; i < block1.getPositionCount(); i++) { outputValues.add(tuple(block1.isNull(i) ? null : block1.getLong(i), block2.isNull(i) ? null : block2.getLong(i))); } + page.releaseBlocks(); }), () -> {} ) @@ -848,6 +884,7 @@ private void assertSortingOnMV( TopNEncoder encoder, TopNOperator.SortOrder... sortOrders ) { + DriverContext driverContext = driverContext(); Block block = TestBlockBuilder.blockFromValues(values, blockType); assert block.mvOrdering() == Block.MvOrdering.UNORDERED : "Blocks created for this test must have unordered multi-values"; Page page = new Page(block); @@ -856,10 +893,11 @@ private void assertSortingOnMV( int topCount = randomIntBetween(1, values.size()); try ( Driver driver = new Driver( - driverContext(), + driverContext, new CannedSourceOperator(List.of(page).iterator()), List.of( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), topCount, List.of(blockType), @@ -878,6 +916,7 @@ private void assertSortingOnMV( } public void testRandomMultiValuesTopN() { + DriverContext driverContext = driverContext(); int rows = randomIntBetween(50, 100); int topCount = randomIntBetween(1, rows); int blocksCount = randomIntBetween(20, 30); @@ -969,8 +1008,9 @@ public void testRandomMultiValuesTopN() { } List>> actualValues = new ArrayList<>(); - List results = this.drive( + List results = drive( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), topCount, elementTypes, @@ -982,6 +1022,7 @@ public void testRandomMultiValuesTopN() { ); for (Page p : results) { readAsRows(actualValues, p); + p.releaseBlocks(); } List>> topNExpectedValues = expectedValues.stream() @@ -1003,13 +1044,15 @@ public void testIPSortingSingleValue() throws UnknownHostException { append(builder, new BytesRef(InetAddressPoint.encode(InetAddress.getByName(ip)))); } + DriverContext driverContext = driverContext(); List> actual = new ArrayList<>(); try ( Driver driver = new Driver( - driverContext(), + driverContext, new CannedSourceOperator(List.of(new Page(builder.build())).iterator()), List.of( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), ips.size(), List.of(BYTES_REF), @@ -1128,12 +1171,14 @@ private void assertIPSortingOnMultiValues( } List> actual = new ArrayList<>(); + DriverContext driverContext = driverContext(); try ( Driver driver = new Driver( - driverContext(), + driverContext, new CannedSourceOperator(List.of(new Page(builder.build())).iterator()), List.of( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), ips.size(), List.of(BYTES_REF), @@ -1210,12 +1255,14 @@ public void testZeroByte() { blocks.add(builderInt.build()); List> actual = new ArrayList<>(); + DriverContext driverContext = driverContext(); try ( Driver driver = new Driver( - driverContext(), + driverContext, new CannedSourceOperator(List.of(new Page(blocks.toArray(Block[]::new))).iterator()), List.of( new TopNOperator( + driverContext.blockFactory(), nonBreakingBigArrays().breakerService().getBreaker("request"), 2, List.of(BYTES_REF, INT), @@ -1243,10 +1290,55 @@ public void testZeroByte() { assertThat((Integer) actual.get(1).get(1), equalTo(100)); } + public void testErrorBeforeFullyDraining() { + int maxPageSize = between(1, 100); + int topCount = maxPageSize * 4; + int docCount = topCount * 10; + List> actual = new ArrayList<>(); + DriverContext driverContext = driverContext(); + try ( + Driver driver = new Driver( + driverContext, + new SequenceLongBlockSourceOperator(driverContext.blockFactory(), LongStream.range(0, docCount)), + List.of( + new TopNOperator( + driverContext.blockFactory(), + nonBreakingBigArrays().breakerService().getBreaker("request"), + topCount, + List.of(LONG), + List.of(DEFAULT_UNSORTABLE), + List.of(new TopNOperator.SortOrder(0, true, randomBoolean())), + maxPageSize + ) + ), + new PageConsumerOperator(p -> { + assertThat(p.getPositionCount(), equalTo(maxPageSize)); + if (actual.isEmpty()) { + readInto(actual, p); + } else { + p.releaseBlocks(); + throw new RuntimeException("boo"); + } + }), + () -> {} + ) + ) { + Exception e = expectThrows(RuntimeException.class, () -> runDriver(driver)); + assertThat(e.getMessage(), equalTo("boo")); + } + + ListMatcher values = matchesList(); + for (int i = 0; i < maxPageSize; i++) { + values = values.item((long) i); + } + assertMap(actual, matchesList().item(values)); + } + public void testCloseWithoutCompleting() { CircuitBreaker breaker = new MockBigArrays.LimitedBreaker(CircuitBreaker.REQUEST, ByteSizeValue.ofGb(1)); try ( TopNOperator op = new TopNOperator( + driverContext().blockFactory(), breaker, 2, List.of(INT), @@ -1257,7 +1349,23 @@ public void testCloseWithoutCompleting() { ) { op.addInput(new Page(new IntArrayVector(new int[] { 1 }, 1).asBlock())); } - assertThat(breaker.getUsed(), equalTo(0L)); + } + + private final List breakers = new ArrayList<>(); + + @Override + protected DriverContext driverContext() { // TODO remove this when the parent uses a breaking block factory + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, ByteSizeValue.ofGb(1)).withCircuitBreaking(); + CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + breakers.add(breaker); + return new DriverContext(bigArrays, new BlockFactory(breaker, bigArrays)); + } + + @After + public void allBreakersEmpty() { + for (CircuitBreaker breaker : breakers) { + assertThat(breaker.getUsed(), equalTo(0L)); + } } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/50_index_patterns.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/50_index_patterns.yml index 280a32aa10cd3..bae0e623d12a3 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/50_index_patterns.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/50_index_patterns.yml @@ -235,35 +235,36 @@ disjoint_mappings: - length: { values: 1 } - match: { values.0.0: 2 } - - do: - esql.query: - body: - query: 'from test1,test2 | sort message1, message2 | eval x = message1, y = message2 + 1 | keep message1, message2, x, y' - - match: { columns.0.name: message1 } - - match: { columns.0.type: keyword } - - match: { columns.1.name: message2 } - - match: { columns.1.type: long } - - match: { columns.2.name: x } - - match: { columns.2.type: keyword } - - match: { columns.3.name: y } - - match: { columns.3.type: long } - - length: { values: 4 } - - match: { values.0.0: foo1 } - - match: { values.0.1: null } - - match: { values.0.2: foo1 } - - match: { values.0.3: null } - - match: { values.1.0: foo2 } - - match: { values.1.1: null } - - match: { values.1.2: foo2 } - - match: { values.1.3: null } - - match: { values.2.0: null } - - match: { values.2.1: 1 } - - match: { values.2.2: null } - - match: { values.2.3: 2 } - - match: { values.3.0: null } - - match: { values.3.1: 2 } - - match: { values.3.2: null } - - match: { values.3.3: 3 } +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +# - do: +# esql.query: +# body: +# query: 'from test1,test2 | sort message1, message2 | eval x = message1, y = message2 + 1 | keep message1, message2, x, y' +# - match: { columns.0.name: message1 } +# - match: { columns.0.type: keyword } +# - match: { columns.1.name: message2 } +# - match: { columns.1.type: long } +# - match: { columns.2.name: x } +# - match: { columns.2.type: keyword } +# - match: { columns.3.name: y } +# - match: { columns.3.type: long } +# - length: { values: 4 } +# - match: { values.0.0: foo1 } +# - match: { values.0.1: null } +# - match: { values.0.2: foo1 } +# - match: { values.0.3: null } +# - match: { values.1.0: foo2 } +# - match: { values.1.1: null } +# - match: { values.1.2: foo2 } +# - match: { values.1.3: null } +# - match: { values.2.0: null } +# - match: { values.2.1: 1 } +# - match: { values.2.2: null } +# - match: { values.2.3: 2 } +# - match: { values.3.0: null } +# - match: { values.3.1: 2 } +# - match: { values.3.2: null } +# - match: { values.3.3: 3 } --- same_name_different_type: diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec index d89f3337c081b..82fd27416c526 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec @@ -7,7 +7,8 @@ emp_no:integer |_index:keyword |_version:long 10002 |employees |1 ; -aliasWithSameName +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +aliasWithSameName-Ignore from employees [metadata _index, _version] | sort emp_no | limit 2 | eval _index = _index, _version = _version | keep emp_no, _index, _version; emp_no:integer |_index:keyword |_version:long diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java index f8aeee1569f2e..800e36949f5ea 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java @@ -9,7 +9,6 @@ import org.elasticsearch.Build; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; @@ -109,32 +108,33 @@ public void testFromStatsGroupingAvgWithAliases() { } private void testFromStatsGroupingAvgImpl(String command, String expectedGroupName, String expectedFieldName) { - EsqlQueryResponse results = run(command); - logger.info(results); - Assert.assertEquals(2, results.columns().size()); - - // assert column metadata - ColumnInfo valuesColumn = results.columns().get(0); - assertEquals(expectedFieldName, valuesColumn.name()); - assertEquals("double", valuesColumn.type()); - ColumnInfo groupColumn = results.columns().get(1); - assertEquals(expectedGroupName, groupColumn.name()); - assertEquals("long", groupColumn.type()); + try (EsqlQueryResponse results = run(command)) { + logger.info(results); + Assert.assertEquals(2, results.columns().size()); - // assert column values - List> valueValues = getValuesList(results); - assertEquals(2, valueValues.size()); - // This is loathsome, find a declarative way to assert the expected output. - if ((long) valueValues.get(0).get(1) == 1L) { - assertEquals(42.0, (double) valueValues.get(0).get(0), 0.0); - assertEquals(2L, (long) valueValues.get(1).get(1)); - assertEquals(44.0, (double) valueValues.get(1).get(0), 0.0); - } else if ((long) valueValues.get(0).get(1) == 2L) { - assertEquals(42.0, (double) valueValues.get(1).get(0), 0.0); - assertEquals(1L, (long) valueValues.get(1).get(1)); - assertEquals(44.0, (double) valueValues.get(0).get(0), 0.0); - } else { - fail("Unexpected group value: " + valueValues.get(0).get(0)); + // assert column metadata + ColumnInfo valuesColumn = results.columns().get(0); + assertEquals(expectedFieldName, valuesColumn.name()); + assertEquals("double", valuesColumn.type()); + ColumnInfo groupColumn = results.columns().get(1); + assertEquals(expectedGroupName, groupColumn.name()); + assertEquals("long", groupColumn.type()); + + // assert column values + List> valueValues = getValuesList(results); + assertEquals(2, valueValues.size()); + // This is loathsome, find a declarative way to assert the expected output. + if ((long) valueValues.get(0).get(1) == 1L) { + assertEquals(42.0, (double) valueValues.get(0).get(0), 0.0); + assertEquals(2L, (long) valueValues.get(1).get(1)); + assertEquals(44.0, (double) valueValues.get(1).get(0), 0.0); + } else if ((long) valueValues.get(0).get(1) == 2L) { + assertEquals(42.0, (double) valueValues.get(1).get(0), 0.0); + assertEquals(1L, (long) valueValues.get(1).get(1)); + assertEquals(44.0, (double) valueValues.get(0).get(0), 0.0); + } else { + fail("Unexpected group value: " + valueValues.get(0).get(0)); + } } } @@ -211,19 +211,20 @@ public void testFromGroupingByNumericFieldWithNulls() { } } client().admin().indices().prepareRefresh("test").get(); - EsqlQueryResponse results = run("from test | stats avg(count) by data | sort data"); - logger.info(results); + try (EsqlQueryResponse results = run("from test | stats avg(count) by data | sort data")) { + logger.info(results); - assertThat(results.columns(), hasSize(2)); - assertEquals("avg(count)", results.columns().get(0).name()); - assertEquals("double", results.columns().get(0).type()); - assertEquals("data", results.columns().get(1).name()); - assertEquals("long", results.columns().get(1).type()); + assertThat(results.columns(), hasSize(2)); + assertEquals("avg(count)", results.columns().get(0).name()); + assertEquals("double", results.columns().get(0).type()); + assertEquals("data", results.columns().get(1).name()); + assertEquals("long", results.columns().get(1).type()); - record Group(Long data, Double avg) {} - List expectedGroups = List.of(new Group(1L, 42.0), new Group(2L, 44.0), new Group(99L, null), new Group(null, 12.0)); - List actualGroups = getValuesList(results).stream().map(l -> new Group((Long) l.get(1), (Double) l.get(0))).toList(); - assertThat(actualGroups, equalTo(expectedGroups)); + record Group(Long data, Double avg) {} + List expectedGroups = List.of(new Group(1L, 42.0), new Group(2L, 44.0), new Group(99L, null), new Group(null, 12.0)); + List actualGroups = getValuesList(results).stream().map(l -> new Group((Long) l.get(1), (Double) l.get(0))).toList(); + assertThat(actualGroups, equalTo(expectedGroups)); + } } public void testFromStatsGroupingByKeyword() { @@ -332,18 +333,19 @@ record Group(double avg, long mi, long ma, long s, long c, String color) {} } public void testFromSortWithTieBreakerLimit() { - EsqlQueryResponse results = run("from test | sort data, count desc, time | limit 5 | keep data, count, time"); - logger.info(results); - assertThat( - getValuesList(results), - contains( - List.of(1L, 44L, epoch + 2), - List.of(1L, 44L, epoch + 6), - List.of(1L, 44L, epoch + 10), - List.of(1L, 44L, epoch + 14), - List.of(1L, 44L, epoch + 18) - ) - ); + try (EsqlQueryResponse results = run("from test | sort data, count desc, time | limit 5 | keep data, count, time")) { + logger.info(results); + assertThat( + getValuesList(results), + contains( + List.of(1L, 44L, epoch + 2), + List.of(1L, 44L, epoch + 6), + List.of(1L, 44L, epoch + 10), + List.of(1L, 44L, epoch + 14), + List.of(1L, 44L, epoch + 18) + ) + ); + } } public void testFromStatsProjectGroup() { @@ -778,10 +780,11 @@ public void testFromStatsLimit() { } public void testFromLimit() { - EsqlQueryResponse results = run("from test | keep data | limit 2"); - logger.info(results); - assertThat(results.columns(), contains(new ColumnInfo("data", "long"))); - assertThat(getValuesList(results), contains(anyOf(contains(1L), contains(2L)), anyOf(contains(1L), contains(2L)))); + try (EsqlQueryResponse results = run("from test | keep data | limit 2")) { + logger.info(results); + assertThat(results.columns(), contains(new ColumnInfo("data", "long"))); + assertThat(getValuesList(results), contains(anyOf(contains(1L), contains(2L)), anyOf(contains(1L), contains(2L)))); + } } public void testDropAllColumns() { @@ -1000,27 +1003,25 @@ public void testTopNPushedToLuceneOnSortedIndex() { ); int limit = randomIntBetween(1, 5); - EsqlQueryResponse results = run("from sorted_test_index | sort time " + sortOrder + " | limit " + limit + " | keep time"); - logger.info(results); - Assert.assertEquals(1, results.columns().size()); - Assert.assertEquals(limit, getValuesList(results).size()); - - // assert column metadata - assertEquals("time", results.columns().get(0).name()); - assertEquals("long", results.columns().get(0).type()); + try (EsqlQueryResponse results = run("from sorted_test_index | sort time " + sortOrder + " | limit " + limit + " | keep time")) { + logger.info(results); + Assert.assertEquals(1, results.columns().size()); + Assert.assertEquals(limit, getValuesList(results).size()); - boolean sortedDesc = "desc".equals(sortOrder); - var expected = LongStream.range(0, 40) - .map(i -> epoch + i) - .boxed() - .sorted(sortedDesc ? reverseOrder() : naturalOrder()) - .limit(limit) - .toList(); - var actual = getValuesList(results).stream().map(l -> (Long) l.get(0)).toList(); - assertThat(actual, equalTo(expected)); + // assert column metadata + assertEquals("time", results.columns().get(0).name()); + assertEquals("long", results.columns().get(0).type()); - // clean-up - client().admin().indices().delete(new DeleteIndexRequest("sorted_test_index")).actionGet(); + boolean sortedDesc = "desc".equals(sortOrder); + var expected = LongStream.range(0, 40) + .map(i -> epoch + i) + .boxed() + .sorted(sortedDesc ? reverseOrder() : naturalOrder()) + .limit(limit) + .toList(); + var actual = getValuesList(results).stream().map(l -> (Long) l.get(0)).toList(); + assertThat(actual, equalTo(expected)); + } } /* diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java index 19893bf8072f1..2636a7cf3c133 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlDisruptionIT.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.esql.action; +import org.apache.lucene.tests.util.LuceneTestCase; import org.elasticsearch.action.ActionFuture; import org.elasticsearch.cluster.coordination.Coordinator; import org.elasticsearch.cluster.coordination.FollowersChecker; @@ -18,6 +19,7 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.disruption.NetworkDisruption; import org.elasticsearch.test.disruption.ServiceDisruptionScheme; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.transport.TransportSettings; @@ -29,7 +31,9 @@ import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +@TestLogging(value = "org.elasticsearch.indices.breaker:TRACE", reason = "failing") @ESIntegTestCase.ClusterScope(scope = TEST, minNumDataNodes = 2, maxNumDataNodes = 4) +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99173") public class EsqlDisruptionIT extends EsqlActionIT { // copied from AbstractDisruptionTestCase From 0fc9fa232c3107308b7a4182136fcfee12a4b007 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 28 Sep 2023 18:43:42 +0100 Subject: [PATCH 133/155] [ML] Reducing use of DiscoveryNode.getVersion (#100024) Remove as much usage as possible of DiscoveryNode.getVersion. Generally for these cases we should be using MlConfigVersion. --- .../ml/action/TrainedModelValidator.java | 2 +- ...ransportStartDataFrameAnalyticsAction.java | 2 +- .../xpack/ml/job/JobNodeSelector.java | 3 +- .../task/OpenJobPersistentTasksExecutor.java | 2 +- .../ml/action/TrainedModelValidatorTests.java | 45 ++++++++++--------- .../xpack/ml/job/JobNodeSelectorTests.java | 35 ++++++++++----- 6 files changed, 54 insertions(+), 35 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TrainedModelValidator.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TrainedModelValidator.java index d1d66299db67f..acd0a124d59c2 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TrainedModelValidator.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TrainedModelValidator.java @@ -59,7 +59,7 @@ static void validateMinimumVersion(ModelPackageConfig resolvedModelPackageConfig if (MlConfigVersion.getMinMlConfigVersion(state.nodes()).before(minimumVersion)) { throw new ActionRequestValidationException().addValidationError( format( - "The model [%s] requires that all nodes are at least version [%s]", + "The model [%s] requires that all nodes have ML config version [%s] or higher", resolvedModelPackageConfig.getPackagedModelId(), resolvedModelPackageConfig.getMinimumVersion() ) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java index 2a1844ea1fccf..ab215106c8ed0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDataFrameAnalyticsAction.java @@ -807,7 +807,7 @@ public static String nodeFilter(DiscoveryNode node, TaskParams params) { + id + "] on node [" + JobNodeSelector.nodeNameAndVersion(node) - + "], because the data frame analytics requires a node of version [" + + "], because the data frame analytics requires a node with ML config version [" + TaskParams.VERSION_INTRODUCED + "] or higher"; } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/JobNodeSelector.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/JobNodeSelector.java index 2997af2e5a1a8..a24e671d1fe25 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/JobNodeSelector.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/JobNodeSelector.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.core.Tuple; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; +import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.autoscaling.NativeMemoryCapacity; import org.elasticsearch.xpack.ml.process.MlMemoryTracker; @@ -346,7 +347,7 @@ static String nodeNameOrId(DiscoveryNode node) { public static String nodeNameAndVersion(DiscoveryNode node) { String nodeNameOrID = nodeNameOrId(node); StringBuilder builder = new StringBuilder("{").append(nodeNameOrID).append('}'); - builder.append('{').append("version=").append(node.getVersion()).append('}'); + builder.append('{').append("ML config version=").append(MlConfigVersion.fromNode(node)).append('}'); return builder.toString(); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/task/OpenJobPersistentTasksExecutor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/task/OpenJobPersistentTasksExecutor.java index b19c0fb670a59..15b1993dc0586 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/task/OpenJobPersistentTasksExecutor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/task/OpenJobPersistentTasksExecutor.java @@ -179,7 +179,7 @@ public static String nodeFilter(DiscoveryNode node, Job job) { + jobId + "] on node [" + JobNodeSelector.nodeNameAndVersion(node) - + "], because the job's model snapshot requires a node of version [" + + "], because the job's model snapshot requires a node with ML config version [" + job.getModelSnapshotMinVersion() + "] or higher"; } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TrainedModelValidatorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TrainedModelValidatorTests.java index d99147e6b4e98..f8755b282c6a1 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TrainedModelValidatorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TrainedModelValidatorTests.java @@ -11,13 +11,16 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ModelPackageConfig; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ModelPackageConfigTests; +import java.net.InetAddress; import java.util.Map; import static org.mockito.Mockito.mock; @@ -31,12 +34,14 @@ public void testValidateMinimumVersion() { .setMinimumVersion("9999.0.0") .build(); - DiscoveryNode node = mock(DiscoveryNode.class); final Map attributes = Map.of(MlConfigVersion.ML_CONFIG_VERSION_NODE_ATTR, MlConfigVersion.CURRENT.toString()); - when(node.getAttributes()).thenReturn(attributes); - when(node.getVersion()).thenReturn(Version.CURRENT); - when(node.getMinIndexVersion()).thenReturn(IndexVersion.current()); - when(node.getId()).thenReturn("node1"); + DiscoveryNode node = DiscoveryNodeUtils.create( + "node1name", + "node1", + new TransportAddress(InetAddress.getLoopbackAddress(), 9301), + attributes, + DiscoveryNodeRole.roles() + ); DiscoveryNodes nodes = DiscoveryNodes.builder().add(node).build(); @@ -52,7 +57,7 @@ public void testValidateMinimumVersion() { assertEquals( "Validation Failed: 1: The model [" + packageConfig.getPackagedModelId() - + "] requires that all nodes are at least version [9999.0.0];", + + "] requires that all nodes have ML config version [9999.0.0] or higher;", e.getMessage() ); } @@ -63,12 +68,11 @@ public void testValidateMinimumVersion() { ModelPackageConfigTests.randomModulePackageConfig() ).setMinimumVersion(MlConfigVersion.CURRENT.toString()).build(); - DiscoveryNode node = mock(DiscoveryNode.class); - final Map attributes = Map.of(MlConfigVersion.ML_CONFIG_VERSION_NODE_ATTR, MlConfigVersion.V_8_7_0.toString()); - when(node.getAttributes()).thenReturn(attributes); - when(node.getVersion()).thenReturn(Version.V_8_7_0); - when(node.getMinIndexVersion()).thenReturn(IndexVersion.current()); - when(node.getId()).thenReturn("node1"); + DiscoveryNode node = DiscoveryNodeUtils.create( + "node1", + new TransportAddress(InetAddress.getLoopbackAddress(), 9300), + Version.V_8_7_0 + ); DiscoveryNodes nodes = DiscoveryNodes.builder().add(node).build(); @@ -82,9 +86,9 @@ public void testValidateMinimumVersion() { assertEquals( "Validation Failed: 1: The model [" + packageConfigCurrent.getPackagedModelId() - + "] requires that all nodes are at least version [" + + "] requires that all nodes have ML config version [" + MlConfigVersion.CURRENT - + "];", + + "] or higher;", e.getMessage() ); } @@ -95,12 +99,11 @@ public void testValidateMinimumVersion() { ModelPackageConfigTests.randomModulePackageConfig() ).setMinimumVersion("_broken_version_").build(); - DiscoveryNode node = mock(DiscoveryNode.class); - final Map attributes = Map.of(MlConfigVersion.ML_CONFIG_VERSION_NODE_ATTR, MlConfigVersion.V_8_7_0.toString()); - when(node.getAttributes()).thenReturn(attributes); - when(node.getVersion()).thenReturn(Version.V_8_7_0); - when(node.getMinIndexVersion()).thenReturn(IndexVersion.current()); - when(node.getId()).thenReturn("node1"); + DiscoveryNode node = DiscoveryNodeUtils.create( + "node1", + new TransportAddress(InetAddress.getLoopbackAddress(), 9300), + Version.V_8_7_0 + ); DiscoveryNodes nodes = DiscoveryNodes.builder().add(node).build(); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/JobNodeSelectorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/JobNodeSelectorTests.java index 19546b37c00cd..ab815aad543b8 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/JobNodeSelectorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/JobNodeSelectorTests.java @@ -42,7 +42,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -81,12 +80,25 @@ public void setup() { when(memoryTracker.getJobMemoryRequirement(anyString(), anyString())).thenReturn(JOB_MEMORY_REQUIREMENT.getBytes()); } - public void testNodeNameAndVersion() { + public void testNodeNameAndVersionForRecentNode() { TransportAddress ta = new TransportAddress(InetAddress.getLoopbackAddress(), 9300); - Map attributes = new HashMap<>(); - attributes.put("unrelated", "attribute"); + Map attributes = Map.of(MlConfigVersion.ML_CONFIG_VERSION_NODE_ATTR, "10.0.0", "unrelated", "attribute"); DiscoveryNode node = DiscoveryNodeUtils.create("_node_name1", "_node_id1", ta, attributes, ROLES_WITHOUT_ML); - assertEquals("{_node_name1}{version=" + node.getVersion() + "}", JobNodeSelector.nodeNameAndVersion(node)); + assertEquals("{_node_name1}{ML config version=10.0.0}", JobNodeSelector.nodeNameAndVersion(node)); + } + + public void testNodeNameAndVersionForOldNode() { + TransportAddress ta = new TransportAddress(InetAddress.getLoopbackAddress(), 9300); + Map attributes = Map.of("unrelated", "attribute"); + DiscoveryNode node = new DiscoveryNode( + "_node_name2", + "_node_id2", + ta, + attributes, + ROLES_WITH_ML, + VersionInformation.inferVersions(Version.V_8_7_0) + ); + assertEquals("{_node_name2}{ML config version=8.7.0}", JobNodeSelector.nodeNameAndVersion(node)); } public void testNodeNameAndMlAttributes() { @@ -869,12 +881,12 @@ public void testSelectLeastLoadedMlNode_reasonsAreInDeterministicOrder() { assertThat( result.getExplanation(), equalTo( - "Not opening job [incompatible_type_job] on node [{_node_name1}{version=" - + Version.CURRENT + "Not opening job [incompatible_type_job] on node [{_node_name1}{ML config version=" + + MlConfigVersion.CURRENT + "}], " + "because this node does not support jobs of type [incompatible_type]|" - + "Not opening job [incompatible_type_job] on node [{_node_name2}{version=" - + Version.CURRENT + + "Not opening job [incompatible_type_job] on node [{_node_name2}{ML config version=" + + MlConfigVersion.CURRENT + "}], " + "because this node does not support jobs of type [incompatible_type]" ) @@ -946,7 +958,10 @@ public void testSelectLeastLoadedMlNode_noNodesMatchingModelSnapshotMinVersion() node -> nodeFilter(node, job) ); PersistentTasksCustomMetadata.Assignment result = jobNodeSelector.selectNode(10, 2, 30, MAX_JOB_BYTES, false); - assertThat(result.getExplanation(), containsString("job's model snapshot requires a node of version [7.3.0] or higher")); + assertThat( + result.getExplanation(), + containsString("job's model snapshot requires a node with ML config version [7.3.0] or higher") + ); assertNull(result.getExecutorNode()); } From f202ad02fe65da38bcbc91d626bb087ca2ce236e Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 28 Sep 2023 18:48:17 +0100 Subject: [PATCH 134/155] GET _data_stream displays both ILM and DSL information (#99947) This add support to the `GET _data_stream` API for displaying the value of the `index.lifecycle.prefer_ilm` setting both at the backing index level and at the top level (top level meaning, similarly to the existing `ilm_policy` field, the value in the index template that's backing the data stream), an `ilm_policy` field for each backing index displaying the actual ILM policy configured for the index itself, a `managed_by` field for each backing index indicating who manages this index (the possible values are: `Index Lifecycle Management`, `Data stream lifecycle`, and `Unmanaged`). This also adds a top level field to indicate which system would manage the next generation index for this data stream based on the current configuration. This field is called `next_generation_managed_by` and the same values as the indices level `managed_by` field has are available. An example output for a data stream that has 2 backing indices managed by ILM and the write index by DSL: ``` { "data_streams": [{ "name": "datastream-psnyudmbitp", "timestamp_field": { "name": "@timestamp" }, "indices": [{ "index_name": ".ds-datastream-psnyudmbitp-2023.09.27-000001", "index_uuid": "kyw0WEXvS8-ahchYS10NRQ", "prefer_ilm": true, "ilm_policy": "policy-uVBEI", "managed_by": "Index Lifecycle Management" }, { "index_name": ".ds-datastream-psnyudmbitp-2023.09.27-000002", "index_uuid": "pDLdc4DERwO54GRzDr4krw", "prefer_ilm": true, "ilm_policy": "policy-uVBEI", "managed_by": "Index Lifecycle Management" }, { "index_name": ".ds-datastream-psnyudmbitp-2023.09.27-000003", "index_uuid": "gYZirLKcS3mlc1c3oHRpYw", "prefer_ilm": false, "ilm_policy": "policy-uVBEI", "managed_by": "Data stream lifecycle" }], "generation": 3, "status": "YELLOW", "template": "indextemplate-obcvkbjqand", "lifecycle": { "enabled": true, "data_retention": "90d" }, "ilm_policy": "policy-uVBEI", "next_generation_managed_by": "Data stream lifecycle", "prefer_ilm": false, "hidden": false, "system": false, "allow_custom_routing": false, "replicated": false }] } ``` --- docs/changelog/99947.yaml | 5 + .../change-mappings-and-settings.asciidoc | 10 +- .../data-streams/downsampling-manual.asciidoc | 6 +- .../indices/get-data-stream.asciidoc | 21 +- .../datastreams/DataStreamIT.java | 10 +- .../action/GetDataStreamsTransportAction.java | 42 ++- .../action/GetDataStreamsResponseTests.java | 240 +++++++++++++++++- .../test/data_stream/10_basic.yml | 71 ++++++ .../org/elasticsearch/TransportVersions.java | 3 +- .../datastreams/GetDataStreamAction.java | 132 +++++++++- .../java/org/elasticsearch/index/Index.java | 7 +- ...ataStreamAndIndexLifecycleMixingTests.java | 127 +++++++++ 12 files changed, 645 insertions(+), 29 deletions(-) create mode 100644 docs/changelog/99947.yaml diff --git a/docs/changelog/99947.yaml b/docs/changelog/99947.yaml new file mode 100644 index 0000000000000..61996c8fde92b --- /dev/null +++ b/docs/changelog/99947.yaml @@ -0,0 +1,5 @@ +pr: 99947 +summary: GET `_data_stream` displays both ILM and DSL information +area: Data streams +type: feature +issues: [] diff --git a/docs/reference/data-streams/change-mappings-and-settings.asciidoc b/docs/reference/data-streams/change-mappings-and-settings.asciidoc index 461addf65c53c..3922ef018a713 100644 --- a/docs/reference/data-streams/change-mappings-and-settings.asciidoc +++ b/docs/reference/data-streams/change-mappings-and-settings.asciidoc @@ -573,15 +573,21 @@ stream's oldest backing index. "indices": [ { "index_name": ".ds-my-data-stream-2099.03.07-000001", <1> - "index_uuid": "Gpdiyq8sRuK9WuthvAdFbw" + "index_uuid": "Gpdiyq8sRuK9WuthvAdFbw", + "prefer_ilm": true, + "managed_by": "Unmanaged" }, { "index_name": ".ds-my-data-stream-2099.03.08-000002", - "index_uuid": "_eEfRrFHS9OyhqWntkgHAQ" + "index_uuid": "_eEfRrFHS9OyhqWntkgHAQ", + "prefer_ilm": true, + "managed_by": "Unmanaged" } ], "generation": 2, "status": "GREEN", + "next_generation_managed_by": "Unmanaged", + "prefer_ilm": true, "template": "my-data-stream-template", "hidden": false, "system": false, diff --git a/docs/reference/data-streams/downsampling-manual.asciidoc b/docs/reference/data-streams/downsampling-manual.asciidoc index 6b98816c2cf56..cc74e98b258de 100644 --- a/docs/reference/data-streams/downsampling-manual.asciidoc +++ b/docs/reference/data-streams/downsampling-manual.asciidoc @@ -358,11 +358,15 @@ This returns: "indices": [ { "index_name": ".ds-my-data-stream-2023.07.26-000001", <1> - "index_uuid": "ltOJGmqgTVm4T-Buoe7Acg" + "index_uuid": "ltOJGmqgTVm4T-Buoe7Acg", + "prefer_ilm": true, + "managed_by": "Data stream lifecycle" } ], "generation": 1, "status": "GREEN", + "next_generation_managed_by": "Data stream lifecycle", + "prefer_ilm": true, "template": "my-data-stream-template", "hidden": false, "system": false, diff --git a/docs/reference/indices/get-data-stream.asciidoc b/docs/reference/indices/get-data-stream.asciidoc index ef2cf7eeee946..36998e7aa5fa3 100644 --- a/docs/reference/indices/get-data-stream.asciidoc +++ b/docs/reference/indices/get-data-stream.asciidoc @@ -225,7 +225,7 @@ cluster can not write into this data stream or change its mappings. `lifecycle`:: (object) -Functionality in preview:[]. Contains the configuration for the data stream lifecycle management of this data stream. +Contains the configuration for the data stream lifecycle management of this data stream. + .Properties of `lifecycle` [%collapsible%open] @@ -265,11 +265,17 @@ The API returns the following response: "indices": [ { "index_name": ".ds-my-data-stream-2099.03.07-000001", - "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg" + "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg", + "prefer_ilm": true, + "ilm_policy": "my-lifecycle-policy", + "managed_by": "Index Lifecycle Management" }, { "index_name": ".ds-my-data-stream-2099.03.08-000002", - "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw" + "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw", + "prefer_ilm": true, + "ilm_policy": "my-lifecycle-policy", + "managed_by": "Index Lifecycle Management" } ], "generation": 2, @@ -277,6 +283,8 @@ The API returns the following response: "my-meta-field": "foo" }, "status": "GREEN", + "next_generation_managed_by": "Index Lifecycle Management", + "prefer_ilm": true, "template": "my-index-template", "ilm_policy": "my-lifecycle-policy", "hidden": false, @@ -292,7 +300,10 @@ The API returns the following response: "indices": [ { "index_name": ".ds-my-data-stream-two-2099.03.08-000001", - "index_uuid": "3liBu2SYS5axasRt6fUIpA" + "index_uuid": "3liBu2SYS5axasRt6fUIpA", + "prefer_ilm": true, + "ilm_policy": "my-lifecycle-policy", + "managed_by": "Index Lifecycle Management" } ], "generation": 1, @@ -300,6 +311,8 @@ The API returns the following response: "my-meta-field": "foo" }, "status": "YELLOW", + "next_generation_managed_by": "Index Lifecycle Management", + "prefer_ilm": true, "template": "my-index-template", "ilm_policy": "my-lifecycle-policy", "hidden": false, diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java index 384970bdc7ab9..bd4a449f640ce 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java @@ -1320,11 +1320,17 @@ public void testGetDataStream() throws Exception { ).actionGet(); assertThat(response.getDataStreams().size(), is(1)); DataStreamInfo metricsFooDataStream = response.getDataStreams().get(0); - assertThat(metricsFooDataStream.getDataStream().getName(), is("metrics-foo")); + DataStream dataStream = metricsFooDataStream.getDataStream(); + assertThat(dataStream.getName(), is("metrics-foo")); assertThat(metricsFooDataStream.getDataStreamStatus(), is(ClusterHealthStatus.YELLOW)); assertThat(metricsFooDataStream.getIndexTemplate(), is("template_for_foo")); assertThat(metricsFooDataStream.getIlmPolicy(), is(nullValue())); - assertThat(metricsFooDataStream.getDataStream().getLifecycle(), is(lifecycle)); + assertThat(dataStream.getLifecycle(), is(lifecycle)); + assertThat(metricsFooDataStream.templatePreferIlmValue(), is(true)); + GetDataStreamAction.Response.IndexProperties indexProperties = metricsFooDataStream.getIndexSettingsValues() + .get(dataStream.getWriteIndex()); + assertThat(indexProperties.ilmPolicyName(), is(nullValue())); + assertThat(indexProperties.preferIlm(), is(true)); } private static void assertBackingIndex(String backingIndex, String timestampFieldPathInMapping, Map expectedMapping) { diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java index 73af952af524d..de81ca9bef18c 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/GetDataStreamsTransportAction.java @@ -11,6 +11,8 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.datastreams.GetDataStreamAction; +import org.elasticsearch.action.datastreams.GetDataStreamAction.Response.IndexProperties; +import org.elasticsearch.action.datastreams.GetDataStreamAction.Response.ManagedBy; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.TransportMasterNodeReadAction; import org.elasticsearch.cluster.ClusterState; @@ -21,6 +23,7 @@ import org.elasticsearch.cluster.metadata.DataStreamLifecycle; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; @@ -39,9 +42,12 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.elasticsearch.index.IndexSettings.PREFER_ILM_SETTING; + public class GetDataStreamsTransportAction extends TransportMasterNodeReadAction< GetDataStreamAction.Request, GetDataStreamAction.Response> { @@ -95,6 +101,7 @@ static GetDataStreamAction.Response innerOperation( List dataStreamInfos = new ArrayList<>(dataStreams.size()); for (DataStream dataStream : dataStreams) { final String indexTemplate; + boolean indexTemplatePreferIlmValue = true; String ilmPolicyName = null; if (dataStream.isSystem()) { SystemDataStreamDescriptor dataStreamDescriptor = systemIndices.findMatchingDataStreamDescriptor(dataStream.getName()); @@ -104,13 +111,15 @@ static GetDataStreamAction.Response innerOperation( dataStreamDescriptor.getComposableIndexTemplate(), dataStreamDescriptor.getComponentTemplates() ); - ilmPolicyName = settings.get("index.lifecycle.name"); + ilmPolicyName = settings.get(IndexMetadata.LIFECYCLE_NAME); + indexTemplatePreferIlmValue = PREFER_ILM_SETTING.get(settings); } } else { indexTemplate = MetadataIndexTemplateService.findV2Template(state.metadata(), dataStream.getName(), false); if (indexTemplate != null) { Settings settings = MetadataIndexTemplateService.resolveSettings(state.metadata(), indexTemplate); - ilmPolicyName = settings.get("index.lifecycle.name"); + ilmPolicyName = settings.get(IndexMetadata.LIFECYCLE_NAME); + indexTemplatePreferIlmValue = PREFER_ILM_SETTING.get(settings); } else { LOGGER.warn( "couldn't find any matching template for data stream [{}]. has it been restored (and possibly renamed)" @@ -125,18 +134,35 @@ static GetDataStreamAction.Response innerOperation( dataStream.getIndices().stream().map(Index::getName).toArray(String[]::new) ); + Map backingIndicesSettingsValues = new HashMap<>(); + Metadata metadata = state.getMetadata(); + for (Index index : dataStream.getIndices()) { + IndexMetadata indexMetadata = metadata.index(index); + Boolean preferIlm = PREFER_ILM_SETTING.get(indexMetadata.getSettings()); + assert preferIlm != null : "must use the default prefer ilm setting value, if nothing else"; + ManagedBy managedBy; + if (metadata.isIndexManagedByILM(indexMetadata)) { + managedBy = ManagedBy.ILM; + } else if (dataStream.isIndexManagedByDataStreamLifecycle(index, metadata::index)) { + managedBy = ManagedBy.LIFECYCLE; + } else { + managedBy = ManagedBy.UNMANAGED; + } + backingIndicesSettingsValues.put(index, new IndexProperties(preferIlm, indexMetadata.getLifecyclePolicyName(), managedBy)); + } + GetDataStreamAction.Response.TimeSeries timeSeries = null; if (dataStream.getIndexMode() == IndexMode.TIME_SERIES) { List> ranges = new ArrayList<>(); Tuple current = null; String previousIndexName = null; for (Index index : dataStream.getIndices()) { - IndexMetadata metadata = state.getMetadata().index(index); - if (metadata.getIndexMode() != IndexMode.TIME_SERIES) { + IndexMetadata indexMetadata = metadata.index(index); + if (indexMetadata.getIndexMode() != IndexMode.TIME_SERIES) { continue; } - Instant start = metadata.getTimeSeriesStart(); - Instant end = metadata.getTimeSeriesEnd(); + Instant start = indexMetadata.getTimeSeriesStart(); + Instant end = indexMetadata.getTimeSeriesEnd(); if (current == null) { current = new Tuple<>(start, end); } else if (current.v2().compareTo(start) == 0) { @@ -175,7 +201,9 @@ static GetDataStreamAction.Response innerOperation( streamHealth.getStatus(), indexTemplate, ilmPolicyName, - timeSeries + timeSeries, + backingIndicesSettingsValues, + indexTemplatePreferIlmValue ) ); } diff --git a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/action/GetDataStreamsResponseTests.java b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/action/GetDataStreamsResponseTests.java index 469c72e539c45..12e1604d10c1f 100644 --- a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/action/GetDataStreamsResponseTests.java +++ b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/action/GetDataStreamsResponseTests.java @@ -8,15 +8,33 @@ package org.elasticsearch.datastreams.action; import org.elasticsearch.action.datastreams.GetDataStreamAction.Response; +import org.elasticsearch.action.datastreams.GetDataStreamAction.Response.ManagedBy; import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.metadata.DataStream; +import org.elasticsearch.cluster.metadata.DataStreamLifecycle; import org.elasticsearch.cluster.metadata.DataStreamTestHelper; +import org.elasticsearch.common.UUIDs; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexMode; import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.xcontent.ToXContent; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentFactory; +import org.elasticsearch.xcontent.XContentParser; +import org.elasticsearch.xcontent.json.JsonXContent; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static org.elasticsearch.cluster.metadata.DataStream.getDefaultBackingIndexName; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; public class GetDataStreamsResponseTests extends AbstractWireSerializingTestCase { @@ -43,13 +61,198 @@ protected Response mutateInstance(Response instance) { return new Response(instance.getDataStreams().stream().map(this::mutateInstance).toList()); } + @SuppressWarnings("unchecked") + public void testResponseIlmAndDataStreamLifecycleRepresentation() throws Exception { + // we'll test a data stream with 3 backing indices - two managed by ILM (having the ILM policy configured for them) + // and one without any ILM policy configured + String dataStreamName = "logs"; + + Index firstGenerationIndex = new Index(getDefaultBackingIndexName(dataStreamName, 1), UUIDs.base64UUID()); + Index secondGenerationIndex = new Index(getDefaultBackingIndexName(dataStreamName, 2), UUIDs.base64UUID()); + Index writeIndex = new Index(getDefaultBackingIndexName(dataStreamName, 3), UUIDs.base64UUID()); + List indices = List.of(firstGenerationIndex, secondGenerationIndex, writeIndex); + { + // data stream has an enabled lifecycle + DataStream logs = new DataStream( + "logs", + indices, + 3, + null, + false, + false, + false, + true, + IndexMode.STANDARD, + new DataStreamLifecycle() + ); + + String ilmPolicyName = "rollover-30days"; + Map indexSettingsValues = Map.of( + firstGenerationIndex, + new Response.IndexProperties(true, ilmPolicyName, ManagedBy.ILM), + secondGenerationIndex, + new Response.IndexProperties(false, ilmPolicyName, ManagedBy.LIFECYCLE), + writeIndex, + new Response.IndexProperties(false, null, ManagedBy.LIFECYCLE) + ); + + Response.DataStreamInfo dataStreamInfo = new Response.DataStreamInfo( + logs, + ClusterHealthStatus.GREEN, + "index-template", + null, + null, + indexSettingsValues, + false + ); + Response response = new Response(List.of(dataStreamInfo)); + XContentBuilder contentBuilder = XContentFactory.jsonBuilder(); + response.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); + + BytesReference bytes = BytesReference.bytes(contentBuilder); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, bytes)) { + Map map = parser.map(); + List dataStreams = (List) map.get(Response.DATA_STREAMS_FIELD.getPreferredName()); + assertThat(dataStreams.size(), is(1)); + Map dataStreamMap = (Map) dataStreams.get(0); + assertThat(dataStreamMap.get(DataStream.NAME_FIELD.getPreferredName()), is(dataStreamName)); + + assertThat(dataStreamMap.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(false)); + assertThat(dataStreamMap.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(nullValue())); + assertThat(dataStreamMap.get(Response.DataStreamInfo.LIFECYCLE_FIELD.getPreferredName()), is(Map.of("enabled", true))); + assertThat( + dataStreamMap.get(Response.DataStreamInfo.NEXT_GENERATION_INDEX_MANAGED_BY.getPreferredName()), + is(ManagedBy.LIFECYCLE.displayValue) + ); + + List indicesRepresentation = (List) dataStreamMap.get(DataStream.INDICES_FIELD.getPreferredName()); + Map firstGenIndexRepresentation = (Map) indicesRepresentation.get(0); + assertThat(firstGenIndexRepresentation.get("index_name"), is(firstGenerationIndex.getName())); + assertThat(firstGenIndexRepresentation.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(true)); + assertThat(firstGenIndexRepresentation.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(ilmPolicyName)); + assertThat( + firstGenIndexRepresentation.get(Response.DataStreamInfo.MANAGED_BY.getPreferredName()), + is(ManagedBy.ILM.displayValue) + ); + + Map secondGenIndexRepresentation = (Map) indicesRepresentation.get(1); + assertThat(secondGenIndexRepresentation.get("index_name"), is(secondGenerationIndex.getName())); + assertThat(secondGenIndexRepresentation.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(false)); + assertThat( + secondGenIndexRepresentation.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), + is(ilmPolicyName) + ); + assertThat( + secondGenIndexRepresentation.get(Response.DataStreamInfo.MANAGED_BY.getPreferredName()), + is(ManagedBy.LIFECYCLE.displayValue) + ); + + // the write index is managed by data stream lifecycle + Map writeIndexRepresentation = (Map) indicesRepresentation.get(2); + assertThat(writeIndexRepresentation.get("index_name"), is(writeIndex.getName())); + assertThat(writeIndexRepresentation.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(false)); + assertThat(writeIndexRepresentation.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(nullValue())); + assertThat( + writeIndexRepresentation.get(Response.DataStreamInfo.MANAGED_BY.getPreferredName()), + is(ManagedBy.LIFECYCLE.displayValue) + ); + } + } + + { + // data stream has a lifecycle that's not enabled + DataStream logs = new DataStream( + "logs", + indices, + 3, + null, + false, + false, + false, + true, + IndexMode.STANDARD, + new DataStreamLifecycle(null, null, false) + ); + + String ilmPolicyName = "rollover-30days"; + Map indexSettingsValues = Map.of( + firstGenerationIndex, + new Response.IndexProperties(true, ilmPolicyName, ManagedBy.ILM), + secondGenerationIndex, + new Response.IndexProperties(true, ilmPolicyName, ManagedBy.ILM), + writeIndex, + new Response.IndexProperties(false, null, ManagedBy.UNMANAGED) + ); + + Response.DataStreamInfo dataStreamInfo = new Response.DataStreamInfo( + logs, + ClusterHealthStatus.GREEN, + "index-template", + null, + null, + indexSettingsValues, + false + ); + Response response = new Response(List.of(dataStreamInfo)); + XContentBuilder contentBuilder = XContentFactory.jsonBuilder(); + response.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); + + BytesReference bytes = BytesReference.bytes(contentBuilder); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, bytes)) { + Map map = parser.map(); + List dataStreams = (List) map.get(Response.DATA_STREAMS_FIELD.getPreferredName()); + assertThat(dataStreams.size(), is(1)); + Map dataStreamMap = (Map) dataStreams.get(0); + assertThat(dataStreamMap.get(DataStream.NAME_FIELD.getPreferredName()), is(dataStreamName)); + // note that the prefer_ilm value is displayed at the top level even if the template backing the data stream doesn't have a + // policy specified anymore + assertThat(dataStreamMap.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(false)); + assertThat(dataStreamMap.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(nullValue())); + assertThat(dataStreamMap.get(Response.DataStreamInfo.LIFECYCLE_FIELD.getPreferredName()), is(Map.of("enabled", false))); + assertThat( + dataStreamMap.get(Response.DataStreamInfo.NEXT_GENERATION_INDEX_MANAGED_BY.getPreferredName()), + is(ManagedBy.UNMANAGED.displayValue) + ); + + List indicesRepresentation = (List) dataStreamMap.get(DataStream.INDICES_FIELD.getPreferredName()); + Map firstGenIndexRepresentation = (Map) indicesRepresentation.get(0); + assertThat(firstGenIndexRepresentation.get("index_name"), is(firstGenerationIndex.getName())); + assertThat(firstGenIndexRepresentation.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(true)); + assertThat(firstGenIndexRepresentation.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(ilmPolicyName)); + assertThat( + firstGenIndexRepresentation.get(Response.DataStreamInfo.MANAGED_BY.getPreferredName()), + is(ManagedBy.ILM.displayValue) + ); + + // the write index is managed by data stream lifecycle + Map writeIndexRepresentation = (Map) indicesRepresentation.get(2); + assertThat(writeIndexRepresentation.get("index_name"), is(writeIndex.getName())); + assertThat(writeIndexRepresentation.get(Response.DataStreamInfo.PREFER_ILM.getPreferredName()), is(false)); + assertThat(writeIndexRepresentation.get(Response.DataStreamInfo.ILM_POLICY_FIELD.getPreferredName()), is(nullValue())); + assertThat( + writeIndexRepresentation.get(Response.DataStreamInfo.MANAGED_BY.getPreferredName()), + is(ManagedBy.UNMANAGED.displayValue) + ); + } + } + } + + public void testManagedByDisplayValuesDontAccidentalyChange() { + // UI might derive logic based on the display values so any changes should be coordinated with the UI team + assertThat(ManagedBy.ILM.displayValue, is("Index Lifecycle Management")); + assertThat(ManagedBy.LIFECYCLE.displayValue, is("Data stream lifecycle")); + assertThat(ManagedBy.UNMANAGED.displayValue, is("Unmanaged")); + } + private Response.DataStreamInfo mutateInstance(Response.DataStreamInfo instance) { var dataStream = instance.getDataStream(); var status = instance.getDataStreamStatus(); var indexTemplate = instance.getIndexTemplate(); var ilmPolicyName = instance.getIlmPolicy(); var timeSeries = instance.getTimeSeries(); - switch (randomIntBetween(0, 4)) { + var indexSettings = instance.getIndexSettingsValues(); + var templatePreferIlm = instance.templatePreferIlmValue(); + switch (randomIntBetween(0, 6)) { case 0 -> dataStream = randomValueOtherThan(dataStream, DataStreamTestHelper::randomInstance); case 1 -> status = randomValueOtherThan(status, () -> randomFrom(ClusterHealthStatus.values())); case 2 -> indexTemplate = randomBoolean() && indexTemplate != null ? null : randomAlphaOfLengthBetween(2, 10); @@ -57,8 +260,22 @@ private Response.DataStreamInfo mutateInstance(Response.DataStreamInfo instance) case 4 -> timeSeries = randomBoolean() && timeSeries != null ? null : randomValueOtherThan(timeSeries, () -> new Response.TimeSeries(generateRandomTimeSeries())); + case 5 -> indexSettings = randomValueOtherThan( + indexSettings, + () -> randomBoolean() + ? Map.of() + : Map.of( + new Index(randomAlphaOfLengthBetween(50, 100), UUIDs.base64UUID()), + new Response.IndexProperties( + randomBoolean(), + randomAlphaOfLengthBetween(50, 100), + randomBoolean() ? ManagedBy.ILM : ManagedBy.LIFECYCLE + ) + ) + ); + case 6 -> templatePreferIlm = templatePreferIlm ? false : true; } - return new Response.DataStreamInfo(dataStream, status, indexTemplate, ilmPolicyName, timeSeries); + return new Response.DataStreamInfo(dataStream, status, indexTemplate, ilmPolicyName, timeSeries, indexSettings, templatePreferIlm); } private List> generateRandomTimeSeries() { @@ -70,6 +287,21 @@ private List> generateRandomTimeSeries() { return timeSeries; } + private Map generateRandomIndexSettingsValues() { + Map values = new HashMap<>(); + for (int i = 0; i < randomIntBetween(0, 3); i++) { + values.put( + new Index(randomAlphaOfLengthBetween(50, 100), UUIDs.base64UUID()), + new Response.IndexProperties( + randomBoolean(), + randomAlphaOfLengthBetween(50, 100), + randomBoolean() ? ManagedBy.ILM : ManagedBy.LIFECYCLE + ) + ); + } + return values; + } + private Response.DataStreamInfo generateRandomDataStreamInfo() { List> timeSeries = randomBoolean() ? generateRandomTimeSeries() : null; return new Response.DataStreamInfo( @@ -77,7 +309,9 @@ private Response.DataStreamInfo generateRandomDataStreamInfo() { ClusterHealthStatus.GREEN, randomAlphaOfLengthBetween(2, 10), randomAlphaOfLengthBetween(2, 10), - timeSeries != null ? new Response.TimeSeries(timeSeries) : null + timeSeries != null ? new Response.TimeSeries(timeSeries) : null, + generateRandomIndexSettingsValues(), + randomBoolean() ); } } diff --git a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml index 50c8e2c74dc74..09cec438d10cc 100644 --- a/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml +++ b/modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/10_basic.yml @@ -311,6 +311,77 @@ setup: name: simple-data-stream2 - is_true: acknowledged +--- +"Get data stream and check DSL and ILM information": + - skip: + version: " - 8.10.99" + reason: "data streams DSL and ILM mixing information available in 8.11+" + + - do: + allowed_warnings: + - "index template [mixing-dsl-template] has index patterns [mixing-dsl-stream] matching patterns from existing older templates + [global] with patterns (global => [*]); this template [mixing-dsl-template] will take precedence during new index creation" + indices.put_index_template: + name: mixing-dsl-template + body: + index_patterns: [mixing-dsl-stream] + template: + mappings: + properties: + '@timestamp': + type: date_nanos + lifecycle: + data_retention: "30d" + enabled: false + settings: + index.lifecycle.prefer_ilm: false + index.lifecycle.name: "missing_ilm_policy" + data_stream: {} + + - do: + indices.create_data_stream: + name: mixing-dsl-stream + - is_true: acknowledged + + - do: + indices.get_data_stream: + name: mixing-dsl-stream + - match: { data_streams.0.name: mixing-dsl-stream } + - match: { data_streams.0.timestamp_field.name: '@timestamp' } + - match: { data_streams.0.generation: 1 } + - match: { data_streams.0.ilm_policy: "missing_ilm_policy" } + - match: { data_streams.0.prefer_ilm: false } + - match: { data_streams.0.next_generation_managed_by: "Index Lifecycle Management" } + - length: { data_streams.0.indices: 1 } + - match: { data_streams.0.indices.0.prefer_ilm: false } + - match: { data_streams.0.indices.0.ilm_policy: "missing_ilm_policy" } + - match: { data_streams.0.indices.0.managed_by: "Index Lifecycle Management" } + + - do: + indices.put_data_lifecycle: + name: "*" + body: > + { + "data_retention": "30d", + "enabled": true + } + + - is_true: acknowledged + + - do: + indices.get_data_stream: + name: mixing-dsl-stream + - match: { data_streams.0.name: mixing-dsl-stream } + - match: { data_streams.0.timestamp_field.name: '@timestamp' } + - match: { data_streams.0.generation: 1 } + - match: { data_streams.0.ilm_policy: "missing_ilm_policy" } + - match: { data_streams.0.prefer_ilm: false } + - match: { data_streams.0.next_generation_managed_by: "Data stream lifecycle" } + - length: { data_streams.0.indices: 1 } + - match: { data_streams.0.indices.0.prefer_ilm: false } + - match: { data_streams.0.indices.0.ilm_policy: "missing_ilm_policy" } + - match: { data_streams.0.indices.0.managed_by: "Data stream lifecycle" } + --- "Delete data stream with backing indices": - skip: diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 5f120134acb04..360af92d6aa43 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -145,8 +145,9 @@ static TransportVersion def(int id) { public static final TransportVersion WAIT_FOR_CLUSTER_STATE_IN_RECOVERY_ADDED = def(8_502_00_0); public static final TransportVersion RECOVERY_COMMIT_TOO_NEW_EXCEPTION_ADDED = def(8_503_00_0); public static final TransportVersion NODE_INFO_COMPONENT_VERSIONS_ADDED = def(8_504_00_0); - public static final TransportVersion COMPACT_FIELD_CAPS_ADDED = def(8_505_00_0); + public static final TransportVersion DATA_STREAM_RESPONSE_INDEX_PROPERTIES = def(8_506_00_0); + /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/server/src/main/java/org/elasticsearch/action/datastreams/GetDataStreamAction.java b/server/src/main/java/org/elasticsearch/action/datastreams/GetDataStreamAction.java index aa69ede54dea1..9c1fb63a6b8d0 100644 --- a/server/src/main/java/org/elasticsearch/action/datastreams/GetDataStreamAction.java +++ b/server/src/main/java/org/elasticsearch/action/datastreams/GetDataStreamAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.Index; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ToXContentObject; @@ -32,8 +33,11 @@ import java.time.Instant; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; +import static org.elasticsearch.TransportVersions.DATA_STREAM_RESPONSE_INDEX_PROPERTIES; + public class GetDataStreamAction extends ActionType { public static final GetDataStreamAction INSTANCE = new GetDataStreamAction(); @@ -142,12 +146,28 @@ public Request includeDefaults(boolean includeDefaults) { } public static class Response extends ActionResponse implements ToXContentObject { + + public enum ManagedBy { + ILM("Index Lifecycle Management"), + LIFECYCLE("Data stream lifecycle"), + UNMANAGED("Unmanaged"); + + public final String displayValue; + + ManagedBy(String displayValue) { + this.displayValue = displayValue; + } + } + public static final ParseField DATA_STREAMS_FIELD = new ParseField("data_streams"); public static class DataStreamInfo implements SimpleDiffable, ToXContentObject { public static final ParseField STATUS_FIELD = new ParseField("status"); public static final ParseField INDEX_TEMPLATE_FIELD = new ParseField("template"); + public static final ParseField PREFER_ILM = new ParseField("prefer_ilm"); + public static final ParseField MANAGED_BY = new ParseField("managed_by"); + public static final ParseField NEXT_GENERATION_INDEX_MANAGED_BY = new ParseField("next_generation_managed_by"); public static final ParseField ILM_POLICY_FIELD = new ParseField("ilm_policy"); public static final ParseField LIFECYCLE_FIELD = new ParseField("lifecycle"); public static final ParseField HIDDEN_FIELD = new ParseField("hidden"); @@ -167,28 +187,39 @@ public static class DataStreamInfo implements SimpleDiffable, To private final String ilmPolicyName; @Nullable private final TimeSeries timeSeries; + private final Map indexSettingsValues; + private final boolean templatePreferIlmValue; public DataStreamInfo( DataStream dataStream, ClusterHealthStatus dataStreamStatus, @Nullable String indexTemplate, @Nullable String ilmPolicyName, - @Nullable TimeSeries timeSeries + @Nullable TimeSeries timeSeries, + Map indexSettingsValues, + boolean templatePreferIlmValue ) { this.dataStream = dataStream; this.dataStreamStatus = dataStreamStatus; this.indexTemplate = indexTemplate; this.ilmPolicyName = ilmPolicyName; this.timeSeries = timeSeries; + this.indexSettingsValues = indexSettingsValues; + this.templatePreferIlmValue = templatePreferIlmValue; } + @SuppressWarnings("unchecked") DataStreamInfo(StreamInput in) throws IOException { this( new DataStream(in), ClusterHealthStatus.readFrom(in), in.readOptionalString(), in.readOptionalString(), - in.getTransportVersion().onOrAfter(TransportVersions.V_8_3_0) ? in.readOptionalWriteable(TimeSeries::new) : null + in.getTransportVersion().onOrAfter(TransportVersions.V_8_3_0) ? in.readOptionalWriteable(TimeSeries::new) : null, + in.getTransportVersion().onOrAfter(DATA_STREAM_RESPONSE_INDEX_PROPERTIES) + ? in.readMap(Index::new, IndexProperties::new) + : Map.of(), + in.getTransportVersion().onOrAfter(DATA_STREAM_RESPONSE_INDEX_PROPERTIES) ? in.readBoolean() : true ); } @@ -215,6 +246,14 @@ public TimeSeries getTimeSeries() { return timeSeries; } + public Map getIndexSettingsValues() { + return indexSettingsValues; + } + + public boolean templatePreferIlmValue() { + return templatePreferIlmValue; + } + @Override public void writeTo(StreamOutput out) throws IOException { dataStream.writeTo(out); @@ -224,6 +263,10 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_3_0)) { out.writeOptionalWriteable(timeSeries); } + if (out.getTransportVersion().onOrAfter(DATA_STREAM_RESPONSE_INDEX_PROPERTIES)) { + out.writeMap(indexSettingsValues); + out.writeBoolean(templatePreferIlmValue); + } } @Override @@ -242,7 +285,27 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params, @Nulla .startObject() .field(DataStream.NAME_FIELD.getPreferredName(), DataStream.TIMESTAMP_FIELD_NAME) .endObject(); - builder.xContentList(DataStream.INDICES_FIELD.getPreferredName(), dataStream.getIndices()); + + builder.field(DataStream.INDICES_FIELD.getPreferredName()); + if (dataStream.getIndices() == null) { + builder.nullValue(); + } else { + builder.startArray(); + for (Index index : dataStream.getIndices()) { + builder.startObject(); + index.toXContentFragment(builder); + IndexProperties indexProperties = indexSettingsValues.get(index); + if (indexProperties != null) { + builder.field(PREFER_ILM.getPreferredName(), indexProperties.preferIlm()); + if (indexProperties.ilmPolicyName() != null) { + builder.field(ILM_POLICY_FIELD.getPreferredName(), indexProperties.ilmPolicyName()); + } + builder.field(MANAGED_BY.getPreferredName(), indexProperties.managedBy.displayValue); + } + builder.endObject(); + } + builder.endArray(); + } builder.field(DataStream.GENERATION_FIELD.getPreferredName(), dataStream.getGeneration()); if (dataStream.getMetadata() != null) { builder.field(DataStream.METADATA_FIELD.getPreferredName(), dataStream.getMetadata()); @@ -258,6 +321,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params, @Nulla if (ilmPolicyName != null) { builder.field(ILM_POLICY_FIELD.getPreferredName(), ilmPolicyName); } + builder.field(NEXT_GENERATION_INDEX_MANAGED_BY.getPreferredName(), getNextGenerationManagedBy().displayValue); + builder.field(PREFER_ILM.getPreferredName(), templatePreferIlmValue); builder.field(HIDDEN_FIELD.getPreferredName(), dataStream.isHidden()); builder.field(SYSTEM_FIELD.getPreferredName(), dataStream.isSystem()); builder.field(ALLOW_CUSTOM_ROUTING.getPreferredName(), dataStream.isAllowCustomRouting()); @@ -280,21 +345,55 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params, @Nulla return builder; } + /** + * Computes and returns which system will manage the next generation for this data stream. + */ + public ManagedBy getNextGenerationManagedBy() { + // both ILM and DSL are configured so let's check the prefer_ilm setting to see which system takes precedence + if (ilmPolicyName != null && dataStream.getLifecycle() != null && dataStream.getLifecycle().isEnabled()) { + return templatePreferIlmValue ? ManagedBy.ILM : ManagedBy.LIFECYCLE; + } + + if (ilmPolicyName != null) { + return ManagedBy.ILM; + } + + if (dataStream.getLifecycle() != null && dataStream.getLifecycle().isEnabled()) { + return ManagedBy.LIFECYCLE; + } + + return ManagedBy.UNMANAGED; + } + @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } DataStreamInfo that = (DataStreamInfo) o; - return dataStream.equals(that.dataStream) + return templatePreferIlmValue == that.templatePreferIlmValue + && Objects.equals(dataStream, that.dataStream) && dataStreamStatus == that.dataStreamStatus && Objects.equals(indexTemplate, that.indexTemplate) && Objects.equals(ilmPolicyName, that.ilmPolicyName) - && Objects.equals(timeSeries, that.timeSeries); + && Objects.equals(timeSeries, that.timeSeries) + && Objects.equals(indexSettingsValues, that.indexSettingsValues); } @Override public int hashCode() { - return Objects.hash(dataStream, dataStreamStatus, indexTemplate, ilmPolicyName, timeSeries); + return Objects.hash( + dataStream, + dataStreamStatus, + indexTemplate, + ilmPolicyName, + timeSeries, + indexSettingsValues, + templatePreferIlmValue + ); } } @@ -326,6 +425,23 @@ public int hashCode() { } } + /** + * Encapsulates the configured properties we want to display for each backing index. + * They'll usually be settings values, but could also be additional properties derived from settings. + */ + public record IndexProperties(boolean preferIlm, @Nullable String ilmPolicyName, ManagedBy managedBy) implements Writeable { + public IndexProperties(StreamInput in) throws IOException { + this(in.readBoolean(), in.readOptionalString(), in.readEnum(ManagedBy.class)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBoolean(preferIlm); + out.writeOptionalString(ilmPolicyName); + out.writeEnum(managedBy); + } + } + private final List dataStreams; @Nullable private final RolloverConfiguration rolloverConfiguration; diff --git a/server/src/main/java/org/elasticsearch/index/Index.java b/server/src/main/java/org/elasticsearch/index/Index.java index 85468326954d6..e11fd394d60a9 100644 --- a/server/src/main/java/org/elasticsearch/index/Index.java +++ b/server/src/main/java/org/elasticsearch/index/Index.java @@ -103,9 +103,14 @@ public void writeTo(final StreamOutput out) throws IOException { @Override public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { builder.startObject(); + toXContentFragment(builder); + return builder.endObject(); + } + + public XContentBuilder toXContentFragment(final XContentBuilder builder) throws IOException { builder.field(INDEX_NAME_KEY, name); builder.field(INDEX_UUID_KEY, uuid); - return builder.endObject(); + return builder; } public static Index fromXContent(final XContentParser parser) throws IOException { diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataStreamAndIndexLifecycleMixingTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataStreamAndIndexLifecycleMixingTests.java index 4086a1a729c14..de355cd675089 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataStreamAndIndexLifecycleMixingTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataStreamAndIndexLifecycleMixingTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.datastreams.CreateDataStreamAction; import org.elasticsearch.action.datastreams.GetDataStreamAction; +import org.elasticsearch.action.datastreams.GetDataStreamAction.Response.ManagedBy; import org.elasticsearch.action.datastreams.lifecycle.ExplainIndexDataStreamLifecycle; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; @@ -56,10 +57,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; +import java.util.function.Function; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.backingIndexEqualTo; import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.DEFAULT_TIMESTAMP_FIELD; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -798,6 +802,129 @@ public void testUpdateIndexTemplateToMigrateFromDataStreamLifecycleToIlm() throw }); } + public void testGetDataStreamResponse() throws Exception { + // ILM rolls over every 2 documents + RolloverAction rolloverIlmAction = new RolloverAction(RolloverConditions.newBuilder().addMaxIndexDocsCondition(2L).build()); + Phase hotPhase = new Phase("hot", TimeValue.ZERO, Map.of(rolloverIlmAction.getWriteableName(), rolloverIlmAction)); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase)); + PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); + assertAcked(client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get()); + + putComposableIndexTemplate( + indexTemplateName, + null, + List.of(dataStreamName + "*"), + Settings.builder().put(LifecycleSettings.LIFECYCLE_NAME, policy).build(), + null, + null + ); + CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName); + client().execute(CreateDataStreamAction.INSTANCE, createDataStreamRequest).get(); + + indexDocs(dataStreamName, 2); + + // wait to rollover + assertBusy(() -> { + GetDataStreamAction.Request getDataStreamRequest = new GetDataStreamAction.Request(new String[] { dataStreamName }); + GetDataStreamAction.Response getDataStreamResponse = client().execute(GetDataStreamAction.INSTANCE, getDataStreamRequest) + .actionGet(); + assertThat(getDataStreamResponse.getDataStreams().size(), equalTo(1)); + assertThat(getDataStreamResponse.getDataStreams().get(0).getDataStream().getIndices().size(), is(2)); + }); + + // prefer_ilm false in the index template + putComposableIndexTemplate( + indexTemplateName, + null, + List.of(dataStreamName + "*"), + Settings.builder().put(LifecycleSettings.LIFECYCLE_NAME, policy).put(IndexSettings.PREFER_ILM, false).build(), + null, + null + ); + + client().execute( + PutDataStreamLifecycleAction.INSTANCE, + new PutDataStreamLifecycleAction.Request(new String[] { dataStreamName }, TimeValue.timeValueDays(90)) + ).actionGet(); + + // rollover again - at this point this data stream should have 2 backing indices managed by ILM and the write index managed by + // data stream lifecycle + indexDocs(dataStreamName, 2); + + assertBusy(() -> { + GetDataStreamAction.Request getDataStreamRequest = new GetDataStreamAction.Request(new String[] { dataStreamName }); + GetDataStreamAction.Response getDataStreamResponse = client().execute(GetDataStreamAction.INSTANCE, getDataStreamRequest) + .actionGet(); + assertThat(getDataStreamResponse.getDataStreams().size(), equalTo(1)); + GetDataStreamAction.Response.DataStreamInfo dataStreamInfo = getDataStreamResponse.getDataStreams().get(0); + List indices = dataStreamInfo.getDataStream().getIndices(); + assertThat(indices.size(), is(3)); + + // the prefer_ilm value from the template should be reflected in the response at the top level + assertThat(dataStreamInfo.templatePreferIlmValue(), is(false)); + // the template ILM policy should still be reflected at the top level + assertThat(dataStreamInfo.getIlmPolicy(), is(policy)); + + List backingIndices = getBackingIndices(dataStreamName); + String firstGenerationIndex = backingIndices.get(0); + String secondGenerationIndex = backingIndices.get(1); + String writeIndex = backingIndices.get(2); + assertThat( + indices.stream().map(i -> i.getName()).toList(), + containsInAnyOrder(firstGenerationIndex, secondGenerationIndex, writeIndex) + ); + + Function> backingIndexSupplier = indexName -> indices.stream() + .filter(index -> index.getName().equals(indexName)) + .findFirst(); + + // let's assert the policy is reported for all indices (as it's present in the index template) and the value of the + // prefer_ilm setting remains true for the first 2 generations and is false for the write index (the generation after rollover) + Optional firstGenSettings = backingIndexSupplier.apply(firstGenerationIndex); + assertThat(firstGenSettings.isPresent(), is(true)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(firstGenSettings.get()).preferIlm(), is(true)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(firstGenSettings.get()).ilmPolicyName(), is(policy)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(firstGenSettings.get()).managedBy(), is(ManagedBy.ILM)); + Optional secondGenSettings = backingIndexSupplier.apply(secondGenerationIndex); + assertThat(secondGenSettings.isPresent(), is(true)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(secondGenSettings.get()).preferIlm(), is(true)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(secondGenSettings.get()).ilmPolicyName(), is(policy)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(secondGenSettings.get()).managedBy(), is(ManagedBy.ILM)); + Optional writeIndexSettings = backingIndexSupplier.apply(writeIndex); + assertThat(writeIndexSettings.isPresent(), is(true)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(writeIndexSettings.get()).preferIlm(), is(false)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(writeIndexSettings.get()).ilmPolicyName(), is(policy)); + assertThat(dataStreamInfo.getIndexSettingsValues().get(writeIndexSettings.get()).managedBy(), is(ManagedBy.LIFECYCLE)); + + // with the current configuratino, the next generation index will be managed by DSL + assertThat(dataStreamInfo.getNextGenerationManagedBy(), is(ManagedBy.LIFECYCLE)); + }); + + // remove ILM policy and prefer_ilm from template + putComposableIndexTemplate(indexTemplateName, null, List.of(dataStreamName + "*"), Settings.builder().build(), null, null); + GetDataStreamAction.Request getDataStreamRequest = new GetDataStreamAction.Request(new String[] { dataStreamName }); + GetDataStreamAction.Response getDataStreamResponse = client().execute(GetDataStreamAction.INSTANCE, getDataStreamRequest) + .actionGet(); + assertThat(getDataStreamResponse.getDataStreams().size(), equalTo(1)); + GetDataStreamAction.Response.DataStreamInfo dataStreamInfo = getDataStreamResponse.getDataStreams().get(0); + // since the ILM related settings are gone from the index template, this data stream should now be managed by lifecycle + assertThat(dataStreamInfo.getNextGenerationManagedBy(), is(ManagedBy.LIFECYCLE)); + + // disable data stream lifecycle on the data stream. the future generations will be UNMANAGED + client().execute( + PutDataStreamLifecycleAction.INSTANCE, + new PutDataStreamLifecycleAction.Request(new String[] { dataStreamName }, TimeValue.timeValueDays(90), false) + ).actionGet(); + + getDataStreamRequest = new GetDataStreamAction.Request(new String[] { dataStreamName }); + getDataStreamResponse = client().execute(GetDataStreamAction.INSTANCE, getDataStreamRequest).actionGet(); + assertThat(getDataStreamResponse.getDataStreams().size(), equalTo(1)); + dataStreamInfo = getDataStreamResponse.getDataStreams().get(0); + // since the ILM related settings are gone from the index template and the lifeclcye is disabled, this data stream should now be + // managed unmanaged + assertThat(dataStreamInfo.getNextGenerationManagedBy(), is(ManagedBy.UNMANAGED)); + } + static void indexDocs(String dataStream, int numDocs) { BulkRequest bulkRequest = new BulkRequest(); for (int i = 0; i < numDocs; i++) { From 7c21ce3f1b461c7a73abd90af58bf7ca53a2b1b2 Mon Sep 17 00:00:00 2001 From: Max Hniebergall <137079448+maxhniebergall@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:56:45 -0400 Subject: [PATCH 135/155] Platform specific models (#99584) * Added platform architecture field to TrainedModelMetadata and users of TrainedModelMetadata * Added TransportVersions guarding for TrainedModelMetadata * Prevent platform-specific models from being deployed on the wrong architecture * Added logic to only verify node architectures for models which are platform specific * Handle null platform architecture * Added logging for the detection of heterogeneous platform architectures among ML nodes and refactoring to support this * Added platform architecture field to TrainedModelConfig * Stop platform-speficic model when rebalance occurs and the cluster has a heterogeneous architecture among ML nodes * Added logic to TransportPutTrainedModelAction to return a warning response header when the model is paltform-specific and cannot be depoloyed on the cluster at that time due to heterogenous architectures among ML nodes * Added MlPlatformArchitecturesUtilTests * Updated Create Trained Models API docs to describe the new platform_architecture optional field. * Updated/incremented InferenceIndexConstants * Added special override to make models with linux-x86_64 in the model ID to be platform specific --- docs/changelog/99584.yaml | 5 + .../apis/put-trained-models.asciidoc | 12 ++ .../org/elasticsearch/TransportVersions.java | 1 + .../core/ml/inference/TrainedModelConfig.java | 39 +++- .../persistence/InferenceIndexConstants.java | 8 +- .../ml/inference/TrainedModelConfigTests.java | 13 +- .../ml/inference_index_mappings.json | 3 + .../xpack/ml/MachineLearning.java | 3 +- .../TransportPutTrainedModelAction.java | 89 ++++++-- .../TrainedModelAssignmentClusterService.java | 159 ++++++++++---- .../deployment/DeploymentManager.java | 52 ++++- .../MlPlatformArchitecturesUtil.java | 127 +++++++++++ .../TransportPutTrainedModelActionTests.java | 70 ++++++ ...nedModelAssignmentClusterServiceTests.java | 178 ++++++++++++++- .../deployment/DeploymentManagerTests.java | 1 + .../MlPlatformArchitecturesUtilTests.java | 202 ++++++++++++++++++ .../xpack/ml/test/MockAppender.java | 36 ++++ .../rest-api-spec/test/ml/inference_crud.yml | 27 +++ 18 files changed, 947 insertions(+), 78 deletions(-) create mode 100644 docs/changelog/99584.yaml create mode 100644 x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtil.java create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtilTests.java create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/test/MockAppender.java diff --git a/docs/changelog/99584.yaml b/docs/changelog/99584.yaml new file mode 100644 index 0000000000000..229e3d8024506 --- /dev/null +++ b/docs/changelog/99584.yaml @@ -0,0 +1,5 @@ +pr: 99584 +summary: Adding an option for trained models to be platform specific +area: Machine Learning +type: enhancement +issues: [] diff --git a/docs/reference/ml/trained-models/apis/put-trained-models.asciidoc b/docs/reference/ml/trained-models/apis/put-trained-models.asciidoc index 82fd1872e6a76..7da46e13a8ce4 100644 --- a/docs/reference/ml/trained-models/apis/put-trained-models.asciidoc +++ b/docs/reference/ml/trained-models/apis/put-trained-models.asciidoc @@ -3,7 +3,9 @@ = Create trained models API [subs="attributes"] ++++ + Create trained models + ++++ Creates a trained model. @@ -1645,6 +1647,16 @@ Appropriate types are: * `pytorch`: The stored definition is a PyTorch (specifically a TorchScript) model. Currently only NLP models are supported. For more information, refer to {ml-docs}/ml-nlp.html[{nlp-cap}]. -- +`platform_architecture`:: +(Optional, string) +If the model only works on one platform, because it is heavily +optimized for a particular processor architecture and OS combination, +then this field specifies which. The format of the string must match +the platform identifiers used by Elasticsearch, so one of, `linux-x86_64`, +`linux-aarch64`, `darwin-x86_64`, `darwin-aarch64`, or `windows-x86_64`. +For portable models (those that work independent of processor architecture or +OS features), leave this field unset. + `tags`:: (Optional, string) diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 360af92d6aa43..30cff2ce427be 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -147,6 +147,7 @@ static TransportVersion def(int id) { public static final TransportVersion NODE_INFO_COMPONENT_VERSIONS_ADDED = def(8_504_00_0); public static final TransportVersion COMPACT_FIELD_CAPS_ADDED = def(8_505_00_0); public static final TransportVersion DATA_STREAM_RESPONSE_INDEX_PROPERTIES = def(8_506_00_0); + public static final TransportVersion ML_TRAINED_MODEL_CONFIG_PLATFORM_ADDED = def(8_507_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java index 9d4442f877b85..9dfa2d51f0fc0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java @@ -103,6 +103,7 @@ public class TrainedModelConfig implements ToXContentObject, Writeable { public static final ParseField PER_DEPLOYMENT_MEMORY_BYTES = new ParseField("per_deployment_memory_bytes"); public static final ParseField PER_ALLOCATION_MEMORY_BYTES = new ParseField("per_allocation_memory_bytes"); + public static final ParseField PLATFORM_ARCHITECTURE = new ParseField("platform_architecture"); public static final TransportVersion VERSION_3RD_PARTY_CONFIG_ADDED = TransportVersions.V_8_0_0; public static final TransportVersion VERSION_ALLOCATION_MEMORY_ADDED = TransportVersions.V_8_500_064; @@ -168,6 +169,7 @@ private static ObjectParser createParser(boole (p, c) -> ignoreUnknownFields ? ModelPackageConfig.fromXContentLenient(p) : ModelPackageConfig.fromXContentStrict(p), MODEL_PACKAGE ); + parser.declareString(TrainedModelConfig.Builder::setPlatformArchitecture, PLATFORM_ARCHITECTURE); return parser; } @@ -195,6 +197,7 @@ public static TrainedModelConfig.Builder fromXContent(XContentParser parser, boo private final TrainedModelLocation location; private final ModelPackageConfig modelPackageConfig; private Boolean fullDefinition; + private String platformArchitecture; TrainedModelConfig( String modelId, @@ -213,7 +216,8 @@ public static TrainedModelConfig.Builder fromXContent(XContentParser parser, boo Map defaultFieldMap, InferenceConfig inferenceConfig, TrainedModelLocation location, - ModelPackageConfig modelPackageConfig + ModelPackageConfig modelPackageConfig, + String platformArchitecture ) { this.modelId = ExceptionsHelper.requireNonNull(modelId, MODEL_ID); this.modelType = modelType; @@ -240,6 +244,7 @@ public static TrainedModelConfig.Builder fromXContent(XContentParser parser, boo this.inferenceConfig = inferenceConfig; this.location = location; this.modelPackageConfig = modelPackageConfig; + this.platformArchitecture = platformArchitecture; } private static TrainedModelInput handleDefaultInput(TrainedModelInput input, TrainedModelType modelType) { @@ -279,6 +284,11 @@ public TrainedModelConfig(StreamInput in) throws IOException { modelPackageConfig = null; fullDefinition = null; } + if (in.getTransportVersion().onOrAfter(TransportVersions.ML_TRAINED_MODEL_CONFIG_PLATFORM_ADDED)) { + platformArchitecture = in.readOptionalString(); + } else { + platformArchitecture = null; + } } public boolean isPackagedModel() { @@ -421,6 +431,10 @@ public long getPerAllocationMemoryBytes() { : 0L; } + public String getPlatformArchitecture() { + return platformArchitecture; + } + @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(modelId); @@ -451,6 +465,10 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalWriteable(modelPackageConfig); out.writeOptionalBoolean(fullDefinition); } + + if (out.getTransportVersion().onOrAfter(TransportVersions.ML_TRAINED_MODEL_CONFIG_PLATFORM_ADDED)) { + out.writeOptionalString(platformArchitecture); + } } @Override @@ -463,6 +481,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (modelPackageConfig != null) { builder.field(MODEL_PACKAGE.getPreferredName(), modelPackageConfig); } + if (platformArchitecture != null) { + builder.field(PLATFORM_ARCHITECTURE.getPreferredName(), platformArchitecture); + } // If the model is to be exported for future import to another cluster, these fields are irrelevant. if (params.paramAsBoolean(EXCLUDE_GENERATED, false) == false) { @@ -543,7 +564,8 @@ public boolean equals(Object o) { && Objects.equals(defaultFieldMap, that.defaultFieldMap) && Objects.equals(inferenceConfig, that.inferenceConfig) && Objects.equals(metadata, that.metadata) - && Objects.equals(location, that.location); + && Objects.equals(location, that.location) + && Objects.equals(platformArchitecture, that.platformArchitecture); } @Override @@ -565,7 +587,8 @@ public int hashCode() { licenseLevel, inferenceConfig, defaultFieldMap, - location + location, + platformArchitecture ); } @@ -590,6 +613,7 @@ public static class Builder { private ModelPackageConfig modelPackageConfig; private Long perDeploymentMemoryBytes; private Long perAllocationMemoryBytes; + private String platformArchitecture; public Builder() {} @@ -611,6 +635,7 @@ public Builder(TrainedModelConfig config) { this.inferenceConfig = config.inferenceConfig; this.location = config.location; this.modelPackageConfig = config.modelPackageConfig; + this.platformArchitecture = config.platformArchitecture; } public Builder setModelId(String modelId) { @@ -703,6 +728,11 @@ public Builder setHyperparameters(List hyperparameters) { return addToMetadata(HYPERPARAMETERS, hyperparameters.stream().map(Hyperparameters::asMap).collect(Collectors.toList())); } + public Builder setPlatformArchitecture(String platformArchitecture) { + this.platformArchitecture = platformArchitecture; + return this; + } + public Builder setModelAliases(Set modelAliases) { if (modelAliases == null || modelAliases.isEmpty()) { return this; @@ -1022,7 +1052,8 @@ public TrainedModelConfig build() { defaultFieldMap, inferenceConfig, location, - modelPackageConfig + modelPackageConfig, + platformArchitecture ); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java index ca70f9e9e761d..6c8fc6fec4e0e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java @@ -42,11 +42,15 @@ public final class InferenceIndexConstants { public static final ParseField DOC_TYPE = new ParseField("doc_type"); private static final String NATIVE_INDEX_PREFIX = INDEX_NAME_PREFIX + "native-"; - private static final String NATIVE_INDEX_VERSION = "000001"; + + // 000002 added support for platform specific models + private static final String NATIVE_INDEX_VERSION = "000002"; private static final String NATIVE_LATEST_INDEX = NATIVE_INDEX_PREFIX + NATIVE_INDEX_VERSION; private static final String MAPPINGS_VERSION_VARIABLE = "xpack.ml.version"; - public static final int INFERENCE_INDEX_MAPPINGS_VERSION = 1; + + // 2 added support for platform specific models + public static final int INFERENCE_INDEX_MAPPINGS_VERSION = 2; public static String mapping() { return TemplateUtils.loadTemplate( diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java index 51f65fe0c9a0b..8b382beeb0644 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.ml.inference; import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -104,7 +105,8 @@ public static TrainedModelConfig.Builder createTestInstance(String modelId, bool .setLicenseLevel(randomFrom(License.OperationMode.PLATINUM.description(), License.OperationMode.BASIC.description())) .setInferenceConfig(randomFrom(inferenceConfigs)) .setTags(tags) - .setLocation(randomBoolean() ? null : IndexLocationTests.randomInstance()); + .setLocation(randomBoolean() ? null : IndexLocationTests.randomInstance()) + .setPlatformArchitecture(randomBoolean() ? null : randomAlphaOfLength(10)); } @Before @@ -191,7 +193,8 @@ public void testToXContentWithParams() throws IOException { .collect(Collectors.toMap(Function.identity(), (k) -> randomAlphaOfLength(10))), randomFrom(ClassificationConfigTests.randomClassificationConfig(), RegressionConfigTests.randomRegressionConfig()), null, - ModelPackageConfigTests.randomModulePackageConfig() + ModelPackageConfigTests.randomModulePackageConfig(), + randomAlphaOfLength(10) ); BytesReference reference = XContentHelper.toXContent(config, XContentType.JSON, ToXContent.EMPTY_PARAMS, false); @@ -241,7 +244,8 @@ public void testParseWithBothDefinitionAndCompressedSupplied() throws IOExceptio .collect(Collectors.toMap(Function.identity(), (k) -> randomAlphaOfLength(10))), randomFrom(ClassificationConfigTests.randomClassificationConfig(), RegressionConfigTests.randomRegressionConfig()), null, - ModelPackageConfigTests.randomModulePackageConfig() + ModelPackageConfigTests.randomModulePackageConfig(), + randomAlphaOfLength(10) ); BytesReference reference = XContentHelper.toXContent(config, XContentType.JSON, ToXContent.EMPTY_PARAMS, false); @@ -453,6 +457,9 @@ protected TrainedModelConfig mutateInstanceForVersion(TrainedModelConfig instanc if (instance.getInferenceConfig() instanceof NlpConfig nlpConfig) { builder.setInferenceConfig(InferenceConfigItemTestCase.mutateForVersion(nlpConfig, version)); } + if (version.before(TransportVersions.ML_TRAINED_MODEL_CONFIG_PLATFORM_ADDED)) { + builder.setPlatformArchitecture(null); + } return builder.build(); } } diff --git a/x-pack/plugin/core/template-resources/src/main/resources/ml/inference_index_mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/ml/inference_index_mappings.json index 7ff961a0aac9c..77634546e0e6e 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/ml/inference_index_mappings.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/ml/inference_index_mappings.json @@ -12,6 +12,9 @@ "model_id": { "type": "keyword" }, + "platform_architecture" : { + "type" : "keyword" + }, "created_by": { "type": "keyword" }, diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index c8e4fd488394a..d2d6bd4fcb443 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -1227,7 +1227,8 @@ public Collection createComponents( threadPool, new NodeLoadDetector(memoryTracker), systemAuditor, - nodeAvailabilityZoneMapper + nodeAvailabilityZoneMapper, + client ) ); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java index 6f97689222196..a0a2a81791550 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.concurrent.EsExecutors; @@ -71,6 +72,7 @@ import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.ml.inference.ModelAliasMetadata; import org.elasticsearch.xpack.ml.inference.assignment.TrainedModelAssignmentMetadata; +import org.elasticsearch.xpack.ml.inference.deployment.MlPlatformArchitecturesUtil; import org.elasticsearch.xpack.ml.inference.persistence.TrainedModelProvider; import org.elasticsearch.xpack.ml.utils.TaskRetriever; @@ -78,6 +80,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -132,7 +135,7 @@ protected void masterOperation( Task task, PutTrainedModelAction.Request request, ClusterState state, - ActionListener listener + ActionListener finalResponseListener ) { TrainedModelConfig config = request.getTrainedModelConfig(); try { @@ -140,7 +143,9 @@ protected void masterOperation( config.ensureParsedDefinition(xContentRegistry); } } catch (IOException ex) { - listener.onFailure(ExceptionsHelper.badRequestException("Failed to parse definition for [{}]", ex, config.getModelId())); + finalResponseListener.onFailure( + ExceptionsHelper.badRequestException("Failed to parse definition for [{}]", ex, config.getModelId()) + ); return; } @@ -150,7 +155,7 @@ protected void masterOperation( try { config.getModelDefinition().getTrainedModel().validate(); } catch (ElasticsearchException ex) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException("Definition for [{}] has validation failures.", ex, config.getModelId()) ); return; @@ -158,7 +163,7 @@ protected void masterOperation( TrainedModelType trainedModelType = TrainedModelType.typeFromTrainedModel(config.getModelDefinition().getTrainedModel()); if (trainedModelType == null) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "Unknown trained model definition class [{}]", config.getModelDefinition().getTrainedModel().getName() @@ -171,7 +176,7 @@ protected void masterOperation( // Set the model type from the definition config = new TrainedModelConfig.Builder(config).setModelType(trainedModelType).build(); } else if (trainedModelType != config.getModelType()) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "{} [{}] does not match the model definition type [{}]", TrainedModelConfig.MODEL_TYPE.getPreferredName(), @@ -183,7 +188,7 @@ protected void masterOperation( } if (config.getInferenceConfig().isTargetTypeSupported(config.getModelDefinition().getTrainedModel().targetType()) == false) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "Model [{}] inference config type [{}] does not support definition target type [{}]", config.getModelId(), @@ -196,7 +201,7 @@ protected void masterOperation( TransportVersion minCompatibilityVersion = config.getModelDefinition().getTrainedModel().getMinimalCompatibilityVersion(); if (state.getMinTransportVersion().before(minCompatibilityVersion)) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "Cannot create model [{}] while cluster upgrade is in progress.", config.getModelId() @@ -223,7 +228,7 @@ protected void masterOperation( } if (ModelAliasMetadata.fromState(state).getModelId(trainedModelConfig.getModelId()) != null) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "requested model_id [{}] is the same as an existing model_alias. Model model_aliases and ids must be unique", config.getModelId() @@ -233,7 +238,7 @@ protected void masterOperation( } if (TrainedModelAssignmentMetadata.fromState(state).hasDeployment(trainedModelConfig.getModelId())) { - listener.onFailure( + finalResponseListener.onFailure( ExceptionsHelper.badRequestException( "Cannot create model [{}] the id is the same as an current model deployment", config.getModelId() @@ -242,6 +247,14 @@ protected void masterOperation( return; } + ActionListener finalResponseAction = ActionListener.wrap((configToReturn) -> { + finalResponseListener.onResponse(new PutTrainedModelAction.Response(configToReturn)); + }, finalResponseListener::onFailure); + + ActionListener verifyClusterAndModelArchitectures = ActionListener.wrap((configToReturn) -> { + verifyMlNodesAndModelArchitectures(configToReturn, client, threadPool, finalResponseAction); + }, finalResponseListener::onFailure); + ActionListener finishedStoringListener = ActionListener.wrap(bool -> { TrainedModelConfig configToReturn = trainedModelConfig.clearDefinition().build(); if (modelPackageConfigHolder.get() != null) { @@ -250,19 +263,19 @@ protected void masterOperation( modelPackageConfigHolder.get(), request.isWaitForCompletion(), ActionListener.wrap( - downloadTriggered -> listener.onResponse(new PutTrainedModelAction.Response(configToReturn)), - listener::onFailure + downloadTriggered -> verifyClusterAndModelArchitectures.onResponse(configToReturn), + finalResponseListener::onFailure ) ); } else { - listener.onResponse(new PutTrainedModelAction.Response(configToReturn)); + finalResponseListener.onResponse(new PutTrainedModelAction.Response(configToReturn)); } - }, listener::onFailure); + }, finalResponseListener::onFailure); var isPackageModel = config.isPackagedModel(); ActionListener checkStorageIndexSizeListener = ActionListener.wrap( r -> trainedModelProvider.storeTrainedModel(trainedModelConfig.build(), finishedStoringListener, isPackageModel), - listener::onFailure + finalResponseListener::onFailure ); ActionListener tagsModelIdCheckListener = ActionListener.wrap(r -> { @@ -276,7 +289,7 @@ protected void masterOperation( IndexStats indexStats = stats.getIndices().get(InferenceIndexConstants.nativeDefinitionStore()); if (indexStats != null && indexStats.getTotal().getStore().getSizeInBytes() > MAX_NATIVE_DEFINITION_INDEX_SIZE.getBytes()) { - listener.onFailure( + finalResponseListener.onFailure( new ElasticsearchStatusException( "Native model store has exceeded the maximum acceptable size of {}, " + "please delete older unused pytorch models", @@ -293,7 +306,7 @@ protected void masterOperation( checkStorageIndexSizeListener.onResponse(null); return; } - listener.onFailure( + finalResponseListener.onFailure( new ElasticsearchStatusException( "Unable to calculate stats for definition storage index [{}], please try again later", RestStatus.SERVICE_UNAVAILABLE, @@ -305,11 +318,11 @@ protected void masterOperation( return; } checkStorageIndexSizeListener.onResponse(null); - }, listener::onFailure); + }, finalResponseListener::onFailure); ActionListener modelIdTagCheckListener = ActionListener.wrap( r -> checkTagsAgainstModelIds(request.getTrainedModelConfig().getTags(), tagsModelIdCheckListener), - listener::onFailure + finalResponseListener::onFailure ); ActionListener handlePackageAndTagsListener = ActionListener.wrap(r -> { @@ -318,29 +331,61 @@ protected void masterOperation( try { TrainedModelValidator.validatePackage(trainedModelConfig, resolvedModelPackageConfig, state); } catch (ValidationException e) { - listener.onFailure(e); + finalResponseListener.onFailure(e); return; } modelPackageConfigHolder.set(resolvedModelPackageConfig); setTrainedModelConfigFieldsFromPackagedModel(trainedModelConfig, resolvedModelPackageConfig, xContentRegistry); checkModelIdAgainstTags(trainedModelConfig.getModelId(), modelIdTagCheckListener); - }, listener::onFailure)); + }, finalResponseListener::onFailure)); } else { checkModelIdAgainstTags(trainedModelConfig.getModelId(), modelIdTagCheckListener); } - }, listener::onFailure); + }, finalResponseListener::onFailure); checkForExistingTask( client, trainedModelConfig.getModelId(), request.isWaitForCompletion(), - listener, + finalResponseListener, handlePackageAndTagsListener, request.timeout() ); } + void verifyMlNodesAndModelArchitectures( + TrainedModelConfig configToReturn, + Client client, + ThreadPool threadPool, + ActionListener configToReturnListener + ) { + ActionListener addWarningHeaderOnFailureListener = new ActionListener() { + @Override + public void onResponse(TrainedModelConfig config) { + assert Objects.equals(config, configToReturn); + configToReturnListener.onResponse(configToReturn); + } + + @Override + public void onFailure(Exception e) { + HeaderWarning.addWarning(e.getMessage()); + configToReturnListener.onResponse(configToReturn); + } + }; + + callVerifyMlNodesAndModelArchitectures(configToReturn, addWarningHeaderOnFailureListener, client, threadPool); + } + + void callVerifyMlNodesAndModelArchitectures( + TrainedModelConfig configToReturn, + ActionListener failureListener, + Client client, + ThreadPool threadPool + ) { + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(failureListener, client, threadPool, configToReturn); + } + /** * This method is package private for testing */ diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java index efc8bd84c6350..ea52c4918d05b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java @@ -15,6 +15,7 @@ import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; @@ -47,6 +48,7 @@ import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.autoscaling.NodeAvailabilityZoneMapper; import org.elasticsearch.xpack.ml.inference.assignment.planning.AllocationReducer; +import org.elasticsearch.xpack.ml.inference.deployment.MlPlatformArchitecturesUtil; import org.elasticsearch.xpack.ml.job.NodeLoad; import org.elasticsearch.xpack.ml.job.NodeLoadDetector; import org.elasticsearch.xpack.ml.notifications.SystemAuditor; @@ -78,6 +80,7 @@ public class TrainedModelAssignmentClusterService implements ClusterStateListene private final NodeLoadDetector nodeLoadDetector; private final SystemAuditor systemAuditor; private final NodeAvailabilityZoneMapper nodeAvailabilityZoneMapper; + private final Client client; private volatile int maxMemoryPercentage; private volatile boolean useAuto; private volatile int maxOpenJobs; @@ -91,7 +94,8 @@ public TrainedModelAssignmentClusterService( ThreadPool threadPool, NodeLoadDetector nodeLoadDetector, SystemAuditor systemAuditor, - NodeAvailabilityZoneMapper nodeAvailabilityZoneMapper + NodeAvailabilityZoneMapper nodeAvailabilityZoneMapper, + Client client ) { this.clusterService = Objects.requireNonNull(clusterService); this.threadPool = Objects.requireNonNull(threadPool); @@ -104,6 +108,7 @@ public TrainedModelAssignmentClusterService( this.maxLazyMLNodes = MachineLearning.MAX_LAZY_ML_NODES.get(settings); this.maxMLNodeSize = MachineLearning.MAX_ML_NODE_SIZE.get(settings).getBytes(); this.allocatedProcessorsScale = MachineLearning.ALLOCATED_PROCESSORS_SCALE.get(settings); + this.client = client; // Only nodes that can possibly be master nodes really need this service running if (DiscoveryNode.isMasterNode(settings)) { clusterService.addListener(this); @@ -150,14 +155,14 @@ private void submitUnbatchedTask(@SuppressWarnings("SameParameterValue") String @Override public void clusterChanged(ClusterChangedEvent event) { - if (event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { + if (eventStateHasGlobalBlockStateNotRecoveredBlock(event)) { return; } if (event.localNodeMaster() == false) { return; } - if (event.state().getMinTransportVersion().before(DISTRIBUTED_MODEL_ALLOCATION_TRANSPORT_VERSION)) { + if (eventStateMinTransportVersionIsBeforeDistributedModelAllocationTransportVersion(event)) { // we should not try to rebalance assignments while there may be nodes running on a version // prior to introducing distributed model allocation. // But we should remove routing to removed or shutting down nodes. @@ -165,6 +170,10 @@ public void clusterChanged(ClusterChangedEvent event) { return; } + if (event.nodesAdded()) { + logMlNodeHeterogeneity(); + } + Optional rebalanceReason = detectReasonToRebalanceModels(event); if (rebalanceReason.isPresent()) { // As this produces a cluster state update task, we are certain that if the persistent @@ -187,6 +196,42 @@ public void clusterChanged(ClusterChangedEvent event) { } } + boolean eventStateMinTransportVersionIsBeforeDistributedModelAllocationTransportVersion(ClusterChangedEvent event) { + return event.state().getMinTransportVersion().before(DISTRIBUTED_MODEL_ALLOCATION_TRANSPORT_VERSION); + } + + boolean eventStateHasGlobalBlockStateNotRecoveredBlock(ClusterChangedEvent event) { + return event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK); + } + + void logMlNodeHeterogeneity() { + ActionListener> architecturesListener = getArchitecturesSetActionListener(); + MlPlatformArchitecturesUtil.getMlNodesArchitecturesSet(architecturesListener, client, threadPool); + } + + static ActionListener> getArchitecturesSetActionListener() { + ActionListener> architecturesListener = new ActionListener>() { + @Override + public void onResponse(Set architectures) { + if (architectures.size() > 1) { + logger.warn( + format( + "Heterogeneous platform architectures were detected among ML nodes. " + + "This will prevent the deployment of some trained models. Distinct platform architectures detected: %s", + architectures + ) + ); + } + } + + @Override + public void onFailure(Exception e) { + logger.error("Failed to detect heterogeneity among ML nodes with exception: ", e); + } + }; + return architecturesListener; + } + private void removeRoutingToRemovedOrShuttingDownNodes(ClusterChangedEvent event) { if (areAssignedNodesRemoved(event)) { submitUnbatchedTask("removing routing entries for removed or shutting down nodes", new ClusterStateUpdateTask() { @@ -486,51 +531,89 @@ private void rebalanceAssignments( String reason, ActionListener listener ) { - threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(() -> { - logger.debug(() -> format("Rebalancing model allocations because [%s]", reason)); - TrainedModelAssignmentMetadata.Builder rebalancedMetadata; - try { - rebalancedMetadata = rebalanceAssignments(clusterState, modelToAdd); - } catch (Exception e) { - listener.onFailure(e); - return; - } + ActionListener> architecturesListener = ActionListener.wrap((mlNodesArchitectures) -> { + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(() -> { + logger.debug(() -> format("Rebalancing model allocations because [%s]", reason)); + + TrainedModelAssignmentMetadata.Builder rebalancedMetadata; + try { + rebalancedMetadata = rebalanceAssignments(clusterState, modelToAdd); + } catch (Exception e) { + listener.onFailure(e); + return; + } - submitUnbatchedTask(reason, new ClusterStateUpdateTask() { + submitUnbatchedTask(reason, new ClusterStateUpdateTask() { - private volatile boolean isUpdated; - private volatile boolean isChanged; + private volatile boolean isUpdated; + private volatile boolean isChanged; - @Override - public ClusterState execute(ClusterState currentState) { + @Override + public ClusterState execute(ClusterState currentState) { - if (areClusterStatesCompatibleForRebalance(clusterState, currentState)) { - isUpdated = true; - ClusterState updatedState = update(currentState, rebalancedMetadata); - isChanged = updatedState != currentState; - return updatedState; + currentState = stopPlatformSpecificModelsInHeterogeneousClusters( + currentState, + mlNodesArchitectures, + modelToAdd, + clusterState + ); + + if (areClusterStatesCompatibleForRebalance(clusterState, currentState)) { + isUpdated = true; + ClusterState updatedState = update(currentState, rebalancedMetadata); + isChanged = updatedState != currentState; + return updatedState; + } + + rebalanceAssignments(currentState, modelToAdd, reason, listener); + return currentState; } - rebalanceAssignments(currentState, modelToAdd, reason, listener); - return currentState; - } - @Override - public void onFailure(Exception e) { - listener.onFailure(e); - } + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } - @Override - public void clusterStateProcessed(ClusterState oldState, ClusterState newState) { - if (isUpdated) { - if (isChanged) { - threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME) - .execute(() -> systemAuditor.info(Messages.getMessage(Messages.INFERENCE_DEPLOYMENT_REBALANCED, reason))); + @Override + public void clusterStateProcessed(ClusterState oldState, ClusterState newState) { + if (isUpdated) { + if (isChanged) { + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME) + .execute( + () -> systemAuditor.info(Messages.getMessage(Messages.INFERENCE_DEPLOYMENT_REBALANCED, reason)) + ); + } + listener.onResponse(TrainedModelAssignmentMetadata.fromState(newState)); } - listener.onResponse(TrainedModelAssignmentMetadata.fromState(newState)); } - } + }); }); - }); + }, listener::onFailure); + + MlPlatformArchitecturesUtil.getMlNodesArchitecturesSet(architecturesListener, client, threadPool); + } + + ClusterState stopPlatformSpecificModelsInHeterogeneousClusters( + ClusterState updatedState, + Set mlNodesArchitectures, + Optional modelToAdd, + ClusterState clusterState + ) { + if (mlNodesArchitectures.size() > 1 && modelToAdd.isPresent()) { + String reasonToStop = format( + "ML nodes in this cluster have multiple platform architectures, " + + "but can only have one for this model ([%s]); " + + "detected architectures: %s", + modelToAdd.get().getModelId(), + mlNodesArchitectures + ); + updatedState = callSetToStopping(reasonToStop, modelToAdd.get().getDeploymentId(), clusterState); + } + return updatedState; + } + + ClusterState callSetToStopping(String reasonToStop, String deploymentId, ClusterState clusterState) { + return setToStopping(clusterState, deploymentId, reasonToStop); } private boolean areClusterStatesCompatibleForRebalance(ClusterState source, ClusterState target) { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java index 03f34dacb1faf..fcb44d0f391fe 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManager.java @@ -164,9 +164,7 @@ public void startDeployment(TrainedModelDeploymentTask task, ActionListener getModelListener = ActionListener.wrap(getModelResponse -> { - assert getModelResponse.getResources().results().size() == 1; - TrainedModelConfig modelConfig = getModelResponse.getResources().results().get(0); + ActionListener getVerifiedModel = ActionListener.wrap((modelConfig) -> { processContext.modelInput.set(modelConfig.getInput()); if (modelConfig.getInferenceConfig() instanceof NlpConfig nlpConfig) { @@ -209,15 +207,57 @@ public void startDeployment(TrainedModelDeploymentTask task, ActionListener verifyModelAndClusterArchitecturesListener = ActionListener.wrap( + getModelResponse -> { + assert getModelResponse.getResources().results().size() == 1; + TrainedModelConfig modelConfig = getModelResponse.getResources().results().get(0); + + verifyMlNodesAndModelArchitectures(modelConfig, client, threadPool, getVerifiedModel); + + }, + failedDeploymentListener::onFailure + ); + executeAsyncWithOrigin( client, ML_ORIGIN, GetTrainedModelsAction.INSTANCE, new GetTrainedModelsAction.Request(task.getParams().getModelId()), - getModelListener + verifyModelAndClusterArchitecturesListener ); } + void verifyMlNodesAndModelArchitectures( + TrainedModelConfig configToReturn, + Client client, + ThreadPool threadPool, + ActionListener configToReturnListener + ) { + ActionListener verifyConfigListener = new ActionListener() { + @Override + public void onResponse(TrainedModelConfig config) { + assert Objects.equals(config, configToReturn); + configToReturnListener.onResponse(configToReturn); + } + + @Override + public void onFailure(Exception e) { + configToReturnListener.onFailure(e); + } + }; + + callVerifyMlNodesAndModelArchitectures(configToReturn, verifyConfigListener, client, threadPool); + } + + void callVerifyMlNodesAndModelArchitectures( + TrainedModelConfig configToReturn, + ActionListener configToReturnListener, + Client client, + ThreadPool threadPool + ) { + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(configToReturnListener, client, threadPool, configToReturn); + } + private SearchRequest vocabSearchRequest(VocabularyConfig vocabularyConfig, String modelId) { return client.prepareSearch(vocabularyConfig.getIndex()) .setQuery(new IdsQueryBuilder().addIds(VocabularyConfig.docId(modelId))) @@ -394,11 +434,11 @@ class ProcessContext { private final PyTorchResultProcessor resultProcessor; private final PyTorchStateStreamer stateStreamer; private final PriorityProcessWorkerExecutorService priorityProcessWorker; + private final AtomicInteger rejectedExecutionCount = new AtomicInteger(); + private final AtomicInteger timeoutCount = new AtomicInteger(); private volatile Instant startTime; private volatile Integer numThreadsPerAllocation; private volatile Integer numAllocations; - private final AtomicInteger rejectedExecutionCount = new AtomicInteger(); - private final AtomicInteger timeoutCount = new AtomicInteger(); private volatile boolean isStopped; private static final TimeValue COMPLETION_TIMEOUT = TimeValue.timeValueMinutes(3); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtil.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtil.java new file mode 100644 index 0000000000000..ff8ac1dbb3eec --- /dev/null +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtil.java @@ -0,0 +1,127 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.inference.deployment; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequestBuilder; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.monitor.os.OsInfo; +import org.elasticsearch.plugins.Platforms; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig; +import org.elasticsearch.xpack.ml.MachineLearning; + +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.elasticsearch.core.Strings.format; +import static org.elasticsearch.xpack.core.ClientHelper.ML_ORIGIN; +import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; + +public class MlPlatformArchitecturesUtil { + + public static void getMlNodesArchitecturesSet(ActionListener> architecturesListener, Client client, ThreadPool threadPool) { + ActionListener listener = MlPlatformArchitecturesUtil.getArchitecturesSetFromNodesInfoResponseListener( + threadPool, + architecturesListener + ); + + NodesInfoRequest request = MlPlatformArchitecturesUtil.getNodesInfoBuilderWithMlNodeArchitectureInfo(client).request(); + executeAsyncWithOrigin(client, ML_ORIGIN, NodesInfoAction.INSTANCE, request, listener); + } + + static ActionListener getArchitecturesSetFromNodesInfoResponseListener( + ThreadPool threadPool, + ActionListener> architecturesListener + ) { + return ActionListener.wrap(nodesInfoResponse -> { + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(() -> { + architecturesListener.onResponse(getArchitecturesSetFromNodesInfoResponse(nodesInfoResponse)); + }); + }, architecturesListener::onFailure); + } + + static NodesInfoRequestBuilder getNodesInfoBuilderWithMlNodeArchitectureInfo(Client client) { + return client.admin().cluster().prepareNodesInfo().clear().setNodesIds("ml:true").setOs(true).setPlugins(true); + } + + private static Set getArchitecturesSetFromNodesInfoResponse(NodesInfoResponse nodesInfoResponse) { + return nodesInfoResponse.getNodes() + .stream() + .filter(node -> node.getNode().hasRole(DiscoveryNodeRole.ML_ROLE.roleName())) + .map(node -> { + OsInfo osInfo = node.getInfo(OsInfo.class); + return Platforms.platformName(osInfo.getName(), osInfo.getArch()); + }) + .collect(Collectors.toUnmodifiableSet()); + } + + public static void verifyMlNodesAndModelArchitectures( + ActionListener successOrFailureListener, + Client client, + ThreadPool threadPool, + TrainedModelConfig configToReturn + ) { + String modelID = configToReturn.getModelId(); + String modelPlatformArchitecture = configToReturn.getPlatformArchitecture(); + + String modifiedPlatformArchitecture = (modelPlatformArchitecture == null && modelID.contains("linux-x86_64")) + ? "linux-x86_64" + : null; + ActionListener> architecturesListener = ActionListener.wrap((architectures) -> { + verifyMlNodesAndModelArchitectures(architectures, modifiedPlatformArchitecture, modelID); + successOrFailureListener.onResponse(configToReturn); + }, successOrFailureListener::onFailure); + + getMlNodesArchitecturesSet(architecturesListener, client, threadPool); + } + + static void verifyMlNodesAndModelArchitectures(Set architectures, String modelPlatformArchitecture, String modelID) + throws IllegalArgumentException, IllegalStateException { + + String architecture = null; + Iterator architecturesIterator = architectures.iterator(); + // If there are no ML nodes at all in the current cluster we assume that any that are added later will work + if (modelPlatformArchitecture == null || architectures.isEmpty() || architecturesIterator.hasNext() == false) { + return; + } + + if (architectures.size() > 1) { + throw new IllegalStateException( + format( + "ML nodes in this cluster have multiple platform architectures, but can only have one for this model ([%s]); " + + "expected [%s]; " + + "but was %s", + modelID, + modelPlatformArchitecture, + architectures + ) + ); + } + + if (Objects.equals(architecturesIterator.next(), modelPlatformArchitecture) == false) { + + throw new IllegalArgumentException( + format( + "The model being deployed ([%s]) is platform specific and incompatible with ML nodes in the cluster; " + + "expected [%s]; " + + "but was %s", + modelID, + modelPlatformArchitecture, + architectures + ) + ); + } + } +} diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelActionTests.java index 514a1e2243531..f708ef1fb2959 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportPutTrainedModelActionTests.java @@ -11,9 +11,12 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; +import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.TimeValue; @@ -21,6 +24,7 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -50,6 +54,7 @@ import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TextSimilarityConfigTests; import org.junit.After; import org.junit.Before; +import org.mockito.ArgumentCaptor; import java.io.IOException; import java.util.Collections; @@ -65,6 +70,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; public class TransportPutTrainedModelActionTests extends ESTestCase { private static final TimeValue TIMEOUT = new TimeValue(30, TimeUnit.SECONDS); @@ -205,6 +215,42 @@ public void testCheckForExistingTaskReturnsTask() { assertThat(returnedModel.getResponse().getModelId(), is(trainedModel.getModelId())); } + public void testVerifyMlNodesAndModelArchitectures_GivenIllegalArgumentException_ThenSetHeaderWarning() { + + TransportPutTrainedModelAction actionSpy = spy(createTransportPutTrainedModelAction()); + @SuppressWarnings("unchecked") + ArgumentCaptor> failureListener = ArgumentCaptor.forClass(ActionListener.class); + @SuppressWarnings("unchecked") + ActionListener mockConfigToReturnListener = mock(ActionListener.class); + TrainedModelConfig mockConfigToReturn = mock(TrainedModelConfig.class); + doNothing().when(mockConfigToReturnListener).onResponse(any()); + + doNothing().when(actionSpy).callVerifyMlNodesAndModelArchitectures(any(), any(), any(), any()); + actionSpy.verifyMlNodesAndModelArchitectures(mockConfigToReturn, null, threadPool, mockConfigToReturnListener); + verify(actionSpy).verifyMlNodesAndModelArchitectures(any(), any(), any(), any()); + verify(actionSpy).callVerifyMlNodesAndModelArchitectures(any(), failureListener.capture(), any(), any()); + + String warningMessage = "TEST HEADER WARNING"; + failureListener.getValue().onFailure(new IllegalArgumentException(warningMessage)); + assertWarnings(warningMessage); + } + + public void testVerifyMlNodesAndModelArchitectures_GivenArchitecturesMatch_ThenTriggerOnResponse() { + + TransportPutTrainedModelAction actionSpy = spy(createTransportPutTrainedModelAction()); + @SuppressWarnings("unchecked") + ArgumentCaptor> successListener = ArgumentCaptor.forClass(ActionListener.class); + @SuppressWarnings("unchecked") + ActionListener mockConfigToReturnListener = mock(ActionListener.class); + TrainedModelConfig mockConfigToReturn = mock(TrainedModelConfig.class); + + doNothing().when(actionSpy).callVerifyMlNodesAndModelArchitectures(any(), any(), any(), any()); + actionSpy.verifyMlNodesAndModelArchitectures(mockConfigToReturn, null, threadPool, mockConfigToReturnListener); + verify(actionSpy).callVerifyMlNodesAndModelArchitectures(any(), successListener.capture(), any(), any()); + + ensureNoWarnings(); + } + private static void prepareGetTrainedModelResponse(Client client, List trainedModels) { doAnswer(invocationOnMock -> { @SuppressWarnings("unchecked") @@ -220,6 +266,30 @@ private static void prepareGetTrainedModelResponse(Client client, List architecturesSet = new HashSet<>(randomList(0, 1, () -> randomAlphaOfLength(10))); + + final ActionListener> underTestListener = TrainedModelAssignmentClusterService.getArchitecturesSetActionListener(); + + underTestListener.onResponse(architecturesSet); + + LogEvent lastEvent = appender.getLastEventAndReset(); + assertNull(lastEvent); + } + + public void testLogMlNodeHeterogeneity_GivenTwoArchitecture_ThenWarn() throws InterruptedException { + String nodeArch = randomAlphaOfLength(10); + Set architecturesSet = Set.of(nodeArch, nodeArch + "2"); // architectures must be different + + final ActionListener> underTestListener = TrainedModelAssignmentClusterService.getArchitecturesSetActionListener(); + underTestListener.onResponse(architecturesSet); + + LogEvent lastEvent = appender.getLastEventAndReset(); + + assertEquals(Level.WARN, lastEvent.getLevel()); + + Message m = lastEvent.getMessage(); + String fm = m.getFormattedMessage(); + String expected = Strings.format( + "Heterogeneous platform architectures were detected among ML nodes. " + + "This will prevent the deployment of some trained models. Distinct platform architectures detected: %s", + architecturesSet + ); + + assertEquals(expected, fm); + } + + public void testLogMlNodeHeterogeneity_GivenFailure_ThenError() throws InterruptedException { + RuntimeException e = new RuntimeException("Test Runtime Exception"); + final ActionListener> underTestListener = TrainedModelAssignmentClusterService.getArchitecturesSetActionListener(); + underTestListener.onFailure(e); + + LogEvent lastEvent = appender.getLastEventAndReset(); + + assertEquals(Level.ERROR, lastEvent.getLevel()); + + Message m = lastEvent.getMessage(); + String fm = m.getFormattedMessage(); + + assertEquals("Failed to detect heterogeneity among ML nodes with exception: ", fm); + assertEquals(e, lastEvent.getThrown()); + } + + public void testClusterChanged_GivenNodesAdded_ThenLogMlNodeHeterogeneityCalled() { + nodeAvailabilityZoneMapper = mock(NodeAvailabilityZoneMapper.class); + TrainedModelAssignmentClusterService serviceSpy = spy(createClusterService(randomInt(5))); + doNothing().when(serviceSpy).logMlNodeHeterogeneity(); + doReturn(false).when(serviceSpy).eventStateHasGlobalBlockStateNotRecoveredBlock(any()); + doReturn(false).when(serviceSpy).eventStateMinTransportVersionIsBeforeDistributedModelAllocationTransportVersion(any()); + + ClusterChangedEvent mockNodesAddedEvent = mock(ClusterChangedEvent.class); + ClusterState mockState = mock(ClusterState.class); + doReturn(mockState).when(mockNodesAddedEvent).state(); + Metadata mockMetadata = mock(Metadata.class); + doReturn(mockMetadata).when(mockState).getMetadata(); + doReturn(null).when(mockState).custom(anyString()); + + doReturn(true).when(mockNodesAddedEvent).localNodeMaster(); + doReturn(true).when(mockNodesAddedEvent).nodesAdded(); + + serviceSpy.clusterChanged(mockNodesAddedEvent); + Mockito.verify(serviceSpy).logMlNodeHeterogeneity(); + Mockito.verify(mockNodesAddedEvent).nodesAdded(); + } + + public void testStopPlatformSpecificModelsInHeterogeneousClusters_GivenMultipleMlNodeArchitectures_ThenCallSetToStopping() { + nodeAvailabilityZoneMapper = mock(NodeAvailabilityZoneMapper.class); + TrainedModelAssignmentClusterService serviceSpy = spy(createClusterService(randomInt(5))); + + Set architecturesSet = new HashSet<>(randomList(2, 5, () -> randomAlphaOfLength(10))); + ClusterState mockUpdatedState = mock(ClusterState.class); + ClusterState mockClusterState = mock(ClusterState.class); + StartTrainedModelDeploymentAction.TaskParams mockModelToAdd = mock(StartTrainedModelDeploymentAction.TaskParams.class); + Optional optionalModelToAdd = Optional.of(mockModelToAdd); + String modelId = randomAlphaOfLength(10); + String deploymentId = randomAlphaOfLength(10); + when(mockModelToAdd.getModelId()).thenReturn(modelId); + when(mockModelToAdd.getDeploymentId()).thenReturn(deploymentId); + + String reasonToStop = format( + "ML nodes in this cluster have multiple platform architectures, " + + "but can only have one for this model ([%s]); " + + "detected architectures: %s", + modelId, + architecturesSet + ); + + doReturn(mockUpdatedState).when(serviceSpy).callSetToStopping(reasonToStop, deploymentId, mockClusterState); + + ClusterState updatedMockClusterState = serviceSpy.stopPlatformSpecificModelsInHeterogeneousClusters( + mockUpdatedState, + architecturesSet, + optionalModelToAdd, + mockClusterState + ); + + verify(serviceSpy).callSetToStopping(reasonToStop, deploymentId, mockClusterState); } public void testUpdateModelRoutingTable() { @@ -1878,7 +2019,8 @@ private TrainedModelAssignmentClusterService createClusterService(int maxLazyNod threadPool, nodeLoadDetector, systemAuditor, - nodeAvailabilityZoneMapper + nodeAvailabilityZoneMapper, + client ); } @@ -1948,4 +2090,36 @@ private static StartTrainedModelDeploymentAction.TaskParams newParams( ); } + protected void assertAsync( + Consumer> function, + T expected, + CheckedConsumer onAnswer, + Consumer onException + ) throws InterruptedException { + + CountDownLatch latch = new CountDownLatch(1); + + LatchedActionListener listener = new LatchedActionListener<>(ActionListener.wrap(r -> { + if (expected == null) { + fail("expected an exception but got a response"); + } else { + assertThat(r, equalTo(expected)); + } + if (onAnswer != null) { + onAnswer.accept(r); + } + }, e -> { + if (onException == null) { + logger.error("got unexpected exception", e); + fail("got unexpected exception: " + e.getMessage()); + } else { + onException.accept(e); + } + }), latch); + + function.accept(listener); + latch.countDown(); + assertTrue("timed out after 20s", latch.await(20, TimeUnit.SECONDS)); + } + } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManagerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManagerTests.java index 0bc898f434030..028c4b48ad355 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManagerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/DeploymentManagerTests.java @@ -108,4 +108,5 @@ public void testRejectedExecution() { assertThat(rejectedCount.intValue(), equalTo(1)); } + } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtilTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtilTests.java new file mode 100644 index 0000000000000..28fc3db10cbe8 --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/deployment/MlPlatformArchitecturesUtilTests.java @@ -0,0 +1,202 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.inference.deployment; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.LatchedActionListener; +import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.core.CheckedConsumer; +import org.elasticsearch.monitor.os.OsInfo; +import org.elasticsearch.plugins.Platforms; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.threadpool.ThreadPool; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MlPlatformArchitecturesUtilTests extends ESTestCase { + + public void testGetNodesOsArchitectures() throws InterruptedException { + var threadPool = mock(ThreadPool.class); + var mockExectutorServervice = mock(ExecutorService.class); + doNothing().when(mockExectutorServervice).execute(any()); + when(threadPool.executor(anyString())).thenReturn(mockExectutorServervice); + + var mockNodesInfoResponse = mock(NodesInfoResponse.class); + List nodeInfoList = randomNodeInfos(4); + when(mockNodesInfoResponse.getNodes()).thenReturn(nodeInfoList); + + var expected = nodeInfoList.stream().filter(node -> node.getNode().hasRole(DiscoveryNodeRole.ML_ROLE.roleName())).map(node -> { + OsInfo osInfo = node.getInfo(OsInfo.class); + return Platforms.platformName(osInfo.getName(), osInfo.getArch()); + }).collect(Collectors.toUnmodifiableSet()); + + assertAsync(new Consumer>>() { + @Override + public void accept(ActionListener> setActionListener) { + final ActionListener nodesInfoResponseActionListener = MlPlatformArchitecturesUtil + .getArchitecturesSetFromNodesInfoResponseListener(threadPool, setActionListener); + nodesInfoResponseActionListener.onResponse(mockNodesInfoResponse); + } + + }, expected, null, null); + } + + public void testVerifyMlNodesAndModelArchitectures_GivenNullModelArchitecture_ThenNothing() { + var architectures = nArchitectures(randomIntBetween(2, 10)); + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(architectures, null, randomAlphaOfLength(10)); + } + + public void testVerifyMlNodesAndModelArchitectures_GivenZeroArches_ThenNothing() { + var architectures = new HashSet(); + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(architectures, randomAlphaOfLength(10), randomAlphaOfLength(10)); + } + + public void testVerifyMlNodesAndModelArchitectures_GivenOneArchMatches_ThenNothing() { + Set architectures = nArchitectures(1); + String architecture = architectures.iterator().next(); + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(architectures, architecture, randomAlphaOfLength(10)); + } + + public void testVerifyMlNodesAndModelArchitectures_GivenAtLeastTwoArches_ThenThrowsISE() { + var architectures = nArchitectures(randomIntBetween(2, 10)); + var modelId = randomAlphaOfLength(10); + var requiredArch = randomAlphaOfLength(10); + String message = "ML nodes in this cluster have multiple platform architectures, " + + "but can only have one for this model ([" + + modelId + + "]); " + + "expected [" + + requiredArch + + "]; but was " + + architectures + + ""; + + Throwable exception = expectThrows( + IllegalStateException.class, + "Expected IllegalStateException but no exception was thrown", + () -> MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(architectures, requiredArch, modelId) + ); + assertEquals(exception.getMessage(), message); + } + + public void testVerifyArchitectureMatchesModelPlatformArchitecture_GivenRequiredArchMatches_ThenNothing() { + var requiredArch = randomAlphaOfLength(10); + + var modelId = randomAlphaOfLength(10); + + MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures( + new HashSet<>(Collections.singleton(requiredArch)), + requiredArch, + modelId + ); + } + + public void testVerifyArchitectureMatchesModelPlatformArchitecture_GivenRequiredArchDoesNotMatch_ThenThrowsIAE() { + var requiredArch = randomAlphaOfLength(10); + String architecturesStr = requiredArch + "-DIFFERENT"; + + var modelId = randomAlphaOfLength(10); + String message = "The model being deployed ([" + + modelId + + "]) is platform specific and incompatible with ML nodes in the cluster; " + + "expected [" + + requiredArch + + "]; but was [" + + architecturesStr + + "]"; + + Throwable exception = expectThrows( + IllegalArgumentException.class, + "Expected IllegalArgumentException but no exception was thrown", + () -> MlPlatformArchitecturesUtil.verifyMlNodesAndModelArchitectures(Set.of(architecturesStr), requiredArch, modelId) + ); + assertEquals(exception.getMessage(), message); + } + + private Set nArchitectures(Integer n) { + Set architectures = new HashSet(n); + for (int i = 0; i < n; i++) { + architectures.add(randomAlphaOfLength(10)); + } + return architectures; + } + + private List randomNodeInfos(int max) { + assertTrue(max > 0); + int n = randomInt(max); + List nodeInfos = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + nodeInfos.add(mockNodeInfo()); + } + return nodeInfos; + } + + private NodeInfo mockNodeInfo() { + var mockNodeInfo = mock(NodeInfo.class); + var mockDiscoveryNode = mock(DiscoveryNode.class); + when(mockNodeInfo.getNode()).thenReturn(mockDiscoveryNode); + when(mockDiscoveryNode.hasRole(DiscoveryNodeRole.ML_ROLE.roleName())).thenReturn(randomBoolean()); + var mockOsInfo = mock(OsInfo.class); + when(mockNodeInfo.getInfo(OsInfo.class)).thenReturn(mockOsInfo); + when(mockOsInfo.getArch()).thenReturn(randomAlphaOfLength(10)); + when(mockOsInfo.getName()).thenReturn(randomAlphaOfLength(10)); + + return mockNodeInfo; + } + + protected void assertAsync( + Consumer> function, + T expected, + CheckedConsumer onAnswer, + Consumer onException + ) throws InterruptedException { + + CountDownLatch latch = new CountDownLatch(1); + + LatchedActionListener listener = new LatchedActionListener<>(ActionListener.wrap(r -> { + if (expected == null) { + fail("expected an exception but got a response"); + } else { + assertThat(r, equalTo(expected)); + } + if (onAnswer != null) { + onAnswer.accept(r); + } + }, e -> { + if (onException == null) { + logger.error("got unexpected exception", e); + fail("got unexpected exception: " + e.getMessage()); + } else { + onException.accept(e); + } + }), latch); + + function.accept(listener); + latch.countDown(); + assertTrue("timed out after 20s", latch.await(20, TimeUnit.SECONDS)); + } +} diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/test/MockAppender.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/test/MockAppender.java new file mode 100644 index 0000000000000..99c3c58f4ee81 --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/test/MockAppender.java @@ -0,0 +1,36 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.test; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.filter.RegexFilter; +import org.apache.logging.log4j.message.Message; + +public class MockAppender extends AbstractAppender { + public LogEvent lastEvent; + + public MockAppender(final String name) throws IllegalAccessException { + super(name, RegexFilter.createFilter(".*(\n.*)*", new String[0], false, null, null), null, false); + } + + @Override + public void append(LogEvent event) { + lastEvent = event.toImmutable(); + } + + Message lastMessage() { + return lastEvent.getMessage(); + } + + public LogEvent getLastEventAndReset() { + LogEvent toReturn = lastEvent; + lastEvent = null; + return toReturn; + } +} diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/inference_crud.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/inference_crud.yml index 417c52e391b7d..b38c6857108cc 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/inference_crud.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/inference_crud.yml @@ -1214,3 +1214,30 @@ setup: ml.get_trained_models: model_id: a-regression-model-0 include: definition_status + +--- +"Test include model platform architecture": + - do: + ml.put_trained_model: + model_id: model-without-definition + body: > + { + "model_type": "pytorch", + "inference_config": { + "ner": { + } + }, + "platform_architecture": "windows-x86_64" + } + + - do: + ml.get_trained_models: + model_id: model-without-definition + include: definition_status + - match: { count: 1 } + - match: { trained_model_configs.0.fully_defined: false } + - do: + ml.get_trained_models: + model_id: model-without-definition + - match: { count: 1 } + - match: { trained_model_configs.0.platform_architecture: windows-x86_64 } From 0ac04ccdd027553a434f2ed3897b5663b35c5d04 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 28 Sep 2023 14:19:50 -0400 Subject: [PATCH 136/155] ESQL: Don't build Pages out of closed Blocks (#99980) This makes sure none of the `Block`s passed to the ctor of `Page` are closed. We never expect to do that. And you can't read the `Block` from the `Page` anyway. --- .../java/org/elasticsearch/compute/data/Page.java | 8 ++++---- .../elasticsearch/compute/data/BasicBlockTests.java | 12 +++++++++--- .../compute/data/BlockFactoryTests.java | 7 +++++-- .../elasticsearch/compute/data/DocVectorTests.java | 12 ++++++++---- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java index 873565592dfaf..a4c89422213b1 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Page.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.core.Assertions; import org.elasticsearch.core.Releasables; import java.io.IOException; @@ -69,9 +68,10 @@ private Page(boolean copyBlocks, int positionCount, Block[] blocks) { // assert assertPositionCount(blocks); this.positionCount = positionCount; this.blocks = copyBlocks ? blocks.clone() : blocks; - if (Assertions.ENABLED) { - for (Block b : blocks) { - assert b.getPositionCount() == positionCount : "expected positionCount=" + positionCount + " but was " + b; + for (Block b : blocks) { + assert b.getPositionCount() == positionCount : "expected positionCount=" + positionCount + " but was " + b; + if (b.isReleased()) { + throw new IllegalArgumentException("can't build page out of released blocks but [" + b + "] was released"); } } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index cf7fbbea1c775..f99ded96a9984 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -906,10 +906,12 @@ void assertZeroPositionsAndRelease(Vector vector) { void releaseAndAssertBreaker(Block... blocks) { assertThat(breaker.getUsed(), greaterThan(0L)); + Page[] pages = Arrays.stream(blocks).map(Page::new).toArray(Page[]::new); Releasables.closeExpectNoException(blocks); Arrays.stream(blocks).forEach(block -> assertThat(block.isReleased(), is(true))); Arrays.stream(blocks).forEach(BasicBlockTests::assertCannotDoubleRelease); - Arrays.stream(blocks).forEach(BasicBlockTests::assertCannotReadFromPage); + Arrays.stream(pages).forEach(BasicBlockTests::assertCannotReadFromPage); + Arrays.stream(blocks).forEach(BasicBlockTests::assertCannotAddToPage); assertThat(breaker.getUsed(), is(0L)); } @@ -924,12 +926,16 @@ static void assertCannotDoubleRelease(Block block) { assertThat(ex.getMessage(), containsString("can't release already released block")); } - static void assertCannotReadFromPage(Block block) { - Page page = new Page(block); + static void assertCannotReadFromPage(Page page) { var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); assertThat(e.getMessage(), containsString("can't read released block")); } + static void assertCannotAddToPage(Block block) { + var e = expectThrows(IllegalArgumentException.class, () -> new Page(block)); + assertThat(e.getMessage(), containsString("can't build page out of released blocks but")); + } + static int randomPosition(int positionCount) { return positionCount == 1 ? 0 : randomIntBetween(0, positionCount - 1); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java index 24d4e27e92ad1..7be79e73b5d9d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -573,13 +573,16 @@ static Block.MvOrdering randomOrdering() { } void releaseAndAssertBreaker(T data) { + Page page = data instanceof Block block ? new Page(block) : null; assertThat(breaker.getUsed(), greaterThan(0L)); Releasables.closeExpectNoException(data); if (data instanceof Block block) { assertThat(block.isReleased(), is(true)); - Page page = new Page(block); - var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); + Exception e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); assertThat(e.getMessage(), containsString("can't read released block")); + + e = expectThrows(IllegalArgumentException.class, () -> new Page(block)); + assertThat(e.getMessage(), containsString("can't build page out of released blocks")); } assertThat(breaker.getUsed(), is(0L)); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java index 0f76e024c7436..465dc95a15ea4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/DocVectorTests.java @@ -136,14 +136,18 @@ public void testCannotDoubleRelease() { var block = new DocVector(IntVector.range(0, 2), IntBlock.newConstantBlockWith(0, 2).asVector(), IntVector.range(0, 2), null) .asBlock(); assertThat(block.isReleased(), is(false)); + Page page = new Page(block); + Releasables.closeExpectNoException(block); assertThat(block.isReleased(), is(true)); - var ex = expectThrows(IllegalStateException.class, () -> block.close()); - assertThat(ex.getMessage(), containsString("can't release already released block")); + Exception e = expectThrows(IllegalStateException.class, () -> block.close()); + assertThat(e.getMessage(), containsString("can't release already released block")); - Page page = new Page(block); - var e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); + e = expectThrows(IllegalStateException.class, () -> page.getBlock(0)); assertThat(e.getMessage(), containsString("can't read released block")); + + e = expectThrows(IllegalArgumentException.class, () -> new Page(block)); + assertThat(e.getMessage(), containsString("can't build page out of released blocks")); } } From c879e54592f388dc19b3de076c318e3b7649ef09 Mon Sep 17 00:00:00 2001 From: Carlos Delgado <6339205+carlosdelest@users.noreply.github.com> Date: Thu, 28 Sep 2023 20:43:06 +0200 Subject: [PATCH 137/155] Allow text_expansion query to use sparse_vector field type (#99910) --- .../ml/integration/TextExpansionQueryIT.java | 56 ++++++--- .../text_expansion_search_rank_features.yml | 111 ++++++++++++++++++ .../text_expansion_search_sparse_vector.yml | 111 ++++++++++++++++++ 3 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_rank_features.yml create mode 100644 x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_sparse_vector.yml diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/TextExpansionQueryIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/TextExpansionQueryIT.java index 7ae7d4b0497e0..dbf489e8abf23 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/TextExpansionQueryIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/TextExpansionQueryIT.java @@ -103,8 +103,16 @@ public class TextExpansionQueryIT extends PyTorchModelRestTestCase { RAW_MODEL_SIZE = Base64.getDecoder().decode(BASE_64_ENCODED_MODEL).length; } + public void testRankFeaturesTextExpansionQuery() throws IOException { + testTextExpansionQuery("rank_features"); + } + + public void testSparseVectorTextExpansionQuery() throws IOException { + testTextExpansionQuery("sparse_vector"); + } + @SuppressWarnings("unchecked") - public void testTextExpansionQuery() throws IOException { + private void testTextExpansionQuery(String tokensFieldType) throws IOException { String modelId = "text-expansion-test"; String indexName = modelId + "-index"; @@ -140,7 +148,7 @@ public void testTextExpansionQuery() throws IOException { } // index tokens - createRankFeaturesIndex(indexName); + createIndex(indexName, tokensFieldType); bulkIndexDocs(inputs, tokenWeights, indexName); // Test text expansion search against the indexed rank features @@ -157,7 +165,15 @@ public void testTextExpansionQuery() throws IOException { } } - public void testWithPipelineIngest() throws IOException { + public void testRankFeaturesWithPipelineIngest() throws IOException { + testWithPipelineIngest("rank_features"); + } + + public void testSparseVectorWithPipelineIngest() throws IOException { + testWithPipelineIngest("sparse_vector"); + } + + private void testWithPipelineIngest(String tokensFieldType) throws IOException { String modelId = "text-expansion-pipeline-test"; String indexName = modelId + "-index"; @@ -182,7 +198,7 @@ public void testWithPipelineIngest() throws IOException { ); // index tokens - createRankFeaturesIndex(indexName); + createIndex(indexName, tokensFieldType); var pipelineId = putPipeline(modelId); bulkIndexThroughPipeline(inputs, indexName, pipelineId); @@ -201,7 +217,15 @@ public void testWithPipelineIngest() throws IOException { } } - public void testWithDotsInTokenNames() throws IOException { + public void testRankFeaturesWithDotsInTokenNames() throws IOException { + testWithDotsInTokenNames("rank_features"); + } + + public void testSparseVectorWithDotsInTokenNames() throws IOException { + testWithDotsInTokenNames("sparse_vector"); + } + + private void testWithDotsInTokenNames(String tokensFieldType) throws IOException { String modelId = "text-expansion-dots-in-tokens"; String indexName = modelId + "-index"; @@ -214,7 +238,7 @@ public void testWithDotsInTokenNames() throws IOException { List inputs = List.of("these are my words."); // index tokens - createRankFeaturesIndex(indexName); + createIndex(indexName, tokensFieldType); var pipelineId = putPipeline(modelId); bulkIndexThroughPipeline(inputs, indexName, pipelineId); @@ -278,18 +302,18 @@ protected void createTextExpansionModel(String modelId) throws IOException { client().performRequest(request); } - private void createRankFeaturesIndex(String indexName) throws IOException { + private void createIndex(String indexName, String tokensFieldType) throws IOException { Request createIndex = new Request("PUT", "/" + indexName); createIndex.setJsonEntity(""" - { - "mappings": { - "properties": { - "text_field": { - "type": "text" - }, - "ml.tokens": { - "type": "rank_features" - } + { + "mappings": { + "properties": { + "text_field": { + "type": "text" + }, + "ml.tokens": { + """ + "\"type\": \"" + tokensFieldType + "\"" + """ + } } } }"""); diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_rank_features.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_rank_features.yml new file mode 100644 index 0000000000000..28a6ad826bc64 --- /dev/null +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_rank_features.yml @@ -0,0 +1,111 @@ +# This test uses the simple model defined in +# TextExpansionQueryIT.java to create the token weights. +setup: + - skip: + version: ' - 8.10.99' + reason: "sparse_vector field type reintroduced in 8.11" + features: headers + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + indices.create: + index: index-with-rank-features + body: + mappings: + properties: + source_text: + type: keyword + ml.tokens: + type: rank_features + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + indices.create: + index: unrelated + body: + mappings: + properties: + source_text: + type: keyword + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model: + model_id: "text_expansion_model" + body: > + { + "description": "simple model for testing", + "model_type": "pytorch", + "inference_config": { + "text_expansion": { + "tokenization": { + "bert": { + "with_special_tokens": false + } + } + } + } + } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model_vocabulary: + model_id: "text_expansion_model" + body: > + { "vocabulary": ["[PAD]", "[UNK]", "these", "are", "my", "words", "the", "washing", "machine", "is", "leaking", "octopus", "comforter", "smells"] } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model_definition_part: + model_id: "text_expansion_model" + part: 0 + body: > + { + "total_definition_length":2078, + "definition": "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", + "total_parts": 1 + } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + Content-Type: application/json + bulk: + index: index-with-rank-features + refresh: true + body: | + {"index": {}} + {"source_text": "my words comforter", "ml.tokens":{"my":1.0, "words":1.0,"comforter":1.0}} + {"index": {}} + {"source_text": "the machine is leaking", "ml.tokens":{"the":1.0,"machine":1.0,"is":1.0,"leaking":1.0}} + {"index": {}} + {"source_text": "these are my words", "ml.tokens":{"these":1.0,"are":1.0,"my":1.0,"words":1.0}} + {"index": {}} + {"source_text": "the octopus comforter smells", "ml.tokens":{"the":1.0,"octopus":1.0,"comforter":1.0,"smells":1.0}} + {"index": {}} + {"source_text": "the octopus comforter is leaking", "ml.tokens":{"the":1.0,"octopus":1.0,"comforter":1.0,"is":1.0,"leaking":1.0}} + {"index": {}} + {"source_text": "washing machine smells", "ml.tokens":{"washing":1.0,"machine":1.0,"smells":1.0}} + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + Content-Type: application/json + ml.start_trained_model_deployment: + model_id: text_expansion_model + wait_for: started + +--- +"Test text expansion search": + - do: + search: + index: index-with-rank-features + body: + query: + text_expansion: + ml.tokens: + model_id: text_expansion_model + model_text: "octopus comforter smells" + - match: { hits.total.value: 4 } + - match: { hits.hits.0._source.source_text: "the octopus comforter smells" } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_sparse_vector.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_sparse_vector.yml new file mode 100644 index 0000000000000..5a31af18f8269 --- /dev/null +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/ml/text_expansion_search_sparse_vector.yml @@ -0,0 +1,111 @@ +# This test uses the simple model defined in +# TextExpansionQueryIT.java to create the token weights. +setup: + - skip: + features: headers + version: ' - 8.7.99' + reason: "text_expansion query introduced in 8.8" + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + indices.create: + index: index-with-rank-features + body: + mappings: + properties: + source_text: + type: keyword + ml.tokens: + type: sparse_vector + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + indices.create: + index: unrelated + body: + mappings: + properties: + source_text: + type: keyword + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model: + model_id: "text_expansion_model" + body: > + { + "description": "simple model for testing", + "model_type": "pytorch", + "inference_config": { + "text_expansion": { + "tokenization": { + "bert": { + "with_special_tokens": false + } + } + } + } + } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model_vocabulary: + model_id: "text_expansion_model" + body: > + { "vocabulary": ["[PAD]", "[UNK]", "these", "are", "my", "words", "the", "washing", "machine", "is", "leaking", "octopus", "comforter", "smells"] } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + ml.put_trained_model_definition_part: + model_id: "text_expansion_model" + part: 0 + body: > + { + "total_definition_length":2078, + "definition": "UEsDBAAACAgAAAAAAAAAAAAAAAAAAAAAAAAUAA4Ac2ltcGxlbW9kZWwvZGF0YS5wa2xGQgoAWlpaWlpaWlpaWoACY19fdG9yY2hfXwpUaW55VGV4dEV4cGFuc2lvbgpxACmBfShYCAAAAHRyYWluaW5ncQGJWBYAAABfaXNfZnVsbF9iYWNrd2FyZF9ob29rcQJOdWJxAy5QSwcIITmbsFgAAABYAAAAUEsDBBQACAgIAAAAAAAAAAAAAAAAAAAAAAAdAB0Ac2ltcGxlbW9kZWwvY29kZS9fX3RvcmNoX18ucHlGQhkAWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWoWRT4+cMAzF7/spfASJomF3e0Ga3nrrn8vcELIyxAzRhAQlpjvbT19DWDrdquqBA/bvPT87nVUxwsm41xPd+PNtUi4a77KvXs+W8voBAHFSQY3EFCIiHKFp1+p57vs/ShyUccZdoIaz93aBTMR+thbPqru+qKBx8P4q/e8TyxRlmwVctJp66H1YmCyS7WsZwD50A2L5V7pCBADGTTOj0bGGE7noQyqzv5JDfp0o9fZRCWqP37yjhE4+mqX5X3AdFZHGM/2TzOHDpy1IvQWR+OWo3KwsRiKdpcqg4pBFDtm+QJ7nqwIPckrlnGfFJG0uNhOl38Sjut3pCqg26QuZy8BR9In7ScHHrKkKMW0TIucFrGQXCMpdaDO05O6DpOiy8e4kr0Ed/2YKOIhplW8gPr4ntygrd9ixpx3j9UZZVRagl2c6+imWUzBjuf5m+Ch7afphuvvW+r/0dsfn+2N9MZGb9+/SFtCYdhd83CMYp+mGy0LiKNs8y/eUuEA8B/d2z4dfUEsHCFSE3IaCAQAAIAMAAFBLAwQUAAgICAAAAAAAAAAAAAAAAAAAAAAAJwApAHNpbXBsZW1vZGVsL2NvZGUvX190b3JjaF9fLnB5LmRlYnVnX3BrbEZCJQBaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpahZHLbtNAFIZtp03rSVIuLRKXjdk5ojitKJsiFq24lem0KKSqpRIZt55gE9/GM+lNLFgx4i1Ys2aHhIBXgAVICNggHgNm6rqJN2BZGv36/v/MOWeea/Z5RVHurLfRUsfZXOnccx522itrd53O0vLqbaKYtsAKUe1pcege7hm9JNtzM8+kOOzNApIX0A3xBXE6YE7g0UWjg2OaZAJXbKvALOnj2GEHKc496ykLktgNt3Jz17hprCUxFqExe7YIpQkNpO1/kfHhPUdtUAdH2/gfmeYiIFW7IkM6IBP2wrDNbMe3Mjf2ksiK3Hjghg7F2DN9l/omZZl5Mmez2QRk0q4WUUB0+1oh9nDwxGdUXJdXPMRZQs352eGaRPV9s2lcMeZFGWBfKJJiw0YgbCMLBaRmXyy4flx6a667Fch55q05QOq2Jg2ANOyZwplhNsjiohVApo7aa21QnNGW5+4GXv8gxK1beBeHSRrhmLXWVh+0aBhErZ7bx1ejxMOhlR6QU4ycNqGyk8/yNGCWkwY7/RCD7UEQek4QszCgDJAzZtfErA0VqHBy9ugQP9pUfUmgCjVYgWNwHFbhBJyEOgSwBuuwARWZmoI6J9PwLfzEocpRpPrT8DP8wqHG0b4UX+E3DiscvRglXIoi81KKPwioHI5x9EooNKWiy0KOc/T6WF4SssrRuzJ9L2VNRXUhJzj6UKYfS4W/q/5wuh/l4M9R9qsU+y2dpoo2hJzkaEET8r6KRONicnRdK9EbUi6raFVIwNGjsrlbpk6ZPi7TbS3fv3LyNjPiEKzG0aG0tvNb6xw90/whe6ONjnJcUxobHDUqQ8bIOW79BVBLBwhfSmPKdAIAAE4EAABQSwMEAAAICAAAAAAAAAAAAAAAAAAAAAAAABkABQBzaW1wbGVtb2RlbC9jb25zdGFudHMucGtsRkIBAFqAAikuUEsHCG0vCVcEAAAABAAAAFBLAwQAAAgIAAAAAAAAAAAAAAAAAAAAAAAAEwA7AHNpbXBsZW1vZGVsL3ZlcnNpb25GQjcAWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWjMKUEsHCNGeZ1UCAAAAAgAAAFBLAQIAAAAACAgAAAAAAAAhOZuwWAAAAFgAAAAUAAAAAAAAAAAAAAAAAAAAAABzaW1wbGVtb2RlbC9kYXRhLnBrbFBLAQIAABQACAgIAAAAAABUhNyGggEAACADAAAdAAAAAAAAAAAAAAAAAKgAAABzaW1wbGVtb2RlbC9jb2RlL19fdG9yY2hfXy5weVBLAQIAABQACAgIAAAAAABfSmPKdAIAAE4EAAAnAAAAAAAAAAAAAAAAAJICAABzaW1wbGVtb2RlbC9jb2RlL19fdG9yY2hfXy5weS5kZWJ1Z19wa2xQSwECAAAAAAgIAAAAAAAAbS8JVwQAAAAEAAAAGQAAAAAAAAAAAAAAAACEBQAAc2ltcGxlbW9kZWwvY29uc3RhbnRzLnBrbFBLAQIAAAAACAgAAAAAAADRnmdVAgAAAAIAAAATAAAAAAAAAAAAAAAAANQFAABzaW1wbGVtb2RlbC92ZXJzaW9uUEsGBiwAAAAAAAAAHgMtAAAAAAAAAAAABQAAAAAAAAAFAAAAAAAAAGoBAAAAAAAAUgYAAAAAAABQSwYHAAAAALwHAAAAAAAAAQAAAFBLBQYAAAAABQAFAGoBAABSBgAAAAA=", + "total_parts": 1 + } + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + Content-Type: application/json + bulk: + index: index-with-rank-features + refresh: true + body: | + {"index": {}} + {"source_text": "my words comforter", "ml.tokens":{"my":1.0, "words":1.0,"comforter":1.0}} + {"index": {}} + {"source_text": "the machine is leaking", "ml.tokens":{"the":1.0,"machine":1.0,"is":1.0,"leaking":1.0}} + {"index": {}} + {"source_text": "these are my words", "ml.tokens":{"these":1.0,"are":1.0,"my":1.0,"words":1.0}} + {"index": {}} + {"source_text": "the octopus comforter smells", "ml.tokens":{"the":1.0,"octopus":1.0,"comforter":1.0,"smells":1.0}} + {"index": {}} + {"source_text": "the octopus comforter is leaking", "ml.tokens":{"the":1.0,"octopus":1.0,"comforter":1.0,"is":1.0,"leaking":1.0}} + {"index": {}} + {"source_text": "washing machine smells", "ml.tokens":{"washing":1.0,"machine":1.0,"smells":1.0}} + + - do: + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + Content-Type: application/json + ml.start_trained_model_deployment: + model_id: text_expansion_model + wait_for: started + +--- +"Test text expansion search": + - do: + search: + index: index-with-rank-features + body: + query: + text_expansion: + ml.tokens: + model_id: text_expansion_model + model_text: "octopus comforter smells" + - match: { hits.total.value: 4 } + - match: { hits.hits.0._source.source_text: "the octopus comforter smells" } From 163b5eff5a6a580975efc7206460e0dd54614de3 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Thu, 28 Sep 2023 12:41:39 -0700 Subject: [PATCH 138/155] Add deduplicated attribute to MvOrdering (#100027) Today, we have the ability to specify whether multivalued fields are sorted in ascending order or not. This feature allows operators like topn to enable optimizations. However, we are currently missing the deduplicated attribute. If multivalued fields are deduplicated at each position, we can further optimize operators such as hash and mv_dedup. In fact, blocks should not have mv_ascending property alone; it always goes together with mv_deduplicated. Additionally, mv_dedup or hash should generate blocks that have only the mv_dedup property. --- .../compute/operator/EvalBenchmark.java | 2 +- .../compute/gen/MvEvaluatorImplementer.java | 2 +- .../operator/MultivalueDedupeBytesRef.java | 6 ++-- .../operator/MultivalueDedupeDouble.java | 6 ++-- .../compute/operator/MultivalueDedupeInt.java | 6 ++-- .../operator/MultivalueDedupeLong.java | 6 ++-- .../operator/topn/KeyExtractorForBoolean.java | 5 ++- .../topn/KeyExtractorForBytesRef.java | 5 ++- .../operator/topn/KeyExtractorForDouble.java | 5 ++- .../operator/topn/KeyExtractorForInt.java | 5 ++- .../operator/topn/KeyExtractorForLong.java | 5 ++- .../blockhash/DoubleBlockHash.java | 3 +- .../aggregation/blockhash/IntBlockHash.java | 2 +- .../aggregation/blockhash/LongBlockHash.java | 3 +- .../compute/data/AbstractVectorBlock.java | 2 +- .../org/elasticsearch/compute/data/Block.java | 32 ++++++++++++++++--- .../compute/data/BlockUtils.java | 20 ++++++++++-- .../compute/lucene/BlockDocValuesReader.java | 18 +++++------ .../operator/X-MultivalueDedupe.java.st | 6 ++-- .../operator/topn/X-KeyExtractor.java.st | 5 ++- .../ValuesSourceReaderOperatorTests.java | 10 +++--- .../operator/topn/TopNOperatorTests.java | 8 ++--- .../multivalue/MvMaxBooleanEvaluator.java | 4 +-- .../multivalue/MvMaxBytesRefEvaluator.java | 4 +-- .../multivalue/MvMaxDoubleEvaluator.java | 4 +-- .../scalar/multivalue/MvMaxIntEvaluator.java | 4 +-- .../scalar/multivalue/MvMaxLongEvaluator.java | 4 +-- .../multivalue/MvMedianIntEvaluator.java | 4 +-- .../multivalue/MvMedianLongEvaluator.java | 4 +-- .../MvMedianUnsignedLongEvaluator.java | 4 +-- .../multivalue/MvMinBooleanEvaluator.java | 4 +-- .../multivalue/MvMinBytesRefEvaluator.java | 4 +-- .../multivalue/MvMinDoubleEvaluator.java | 4 +-- .../scalar/multivalue/MvMinIntEvaluator.java | 4 +-- .../scalar/multivalue/MvMinLongEvaluator.java | 4 +-- .../AbstractMultivalueFunctionTestCase.java | 14 +++++++- 36 files changed, 137 insertions(+), 91 deletions(-) diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java index 82c9416515d24..e129cdaa12469 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java @@ -229,7 +229,7 @@ private static Page page(String operation) { case "mv_min", "mv_min_ascending" -> { var builder = LongBlock.newBlockBuilder(BLOCK_LENGTH); if (operation.endsWith("ascending")) { - builder.mvOrdering(Block.MvOrdering.ASCENDING); + builder.mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } for (int i = 0; i < BLOCK_LENGTH; i++) { builder.beginPositionEntry(); diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java index 86ae6d3f46789..0e29cc7673fee 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java @@ -261,7 +261,7 @@ private MethodSpec eval(String name, boolean nullable) { if (ascendingFunction == null) { return; } - builder.beginControlFlow("if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING)"); + builder.beginControlFlow("if (fieldVal.mvSortedAscending())"); builder.addStatement("return $L(fieldVal)", name.replace("eval", "evalAscending")); builder.endControlFlow(); }, builder -> { diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeBytesRef.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeBytesRef.java index 48aec38b800ce..89c15d9eeab72 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeBytesRef.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeBytesRef.java @@ -44,7 +44,7 @@ public MultivalueDedupeBytesRef(BytesRefBlock block) { * {@link Block} using an adaptive algorithm based on the size of the input list. */ public BytesRefBlock dedupeToBlockAdaptive() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(block.getPositionCount()); @@ -92,7 +92,7 @@ public BytesRefBlock dedupeToBlockAdaptive() { * which picks based on the number of elements at each position. */ public BytesRefBlock dedupeToBlockUsingCopyAndSort() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(block.getPositionCount()); @@ -120,7 +120,7 @@ public BytesRefBlock dedupeToBlockUsingCopyAndSort() { * {@link #dedupeToBlockAdaptive} unless you need the results sorted. */ public BytesRefBlock dedupeToBlockUsingCopyMissing() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(block.getPositionCount()); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeDouble.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeDouble.java index d30292f6fa32c..22f5cef2d57d8 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeDouble.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeDouble.java @@ -41,7 +41,7 @@ public MultivalueDedupeDouble(DoubleBlock block) { * {@link Block} using an adaptive algorithm based on the size of the input list. */ public DoubleBlock dedupeToBlockAdaptive() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(block.getPositionCount()); @@ -89,7 +89,7 @@ public DoubleBlock dedupeToBlockAdaptive() { * which picks based on the number of elements at each position. */ public DoubleBlock dedupeToBlockUsingCopyAndSort() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(block.getPositionCount()); @@ -117,7 +117,7 @@ public DoubleBlock dedupeToBlockUsingCopyAndSort() { * {@link #dedupeToBlockAdaptive} unless you need the results sorted. */ public DoubleBlock dedupeToBlockUsingCopyMissing() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(block.getPositionCount()); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeInt.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeInt.java index cda9308a7e6d2..be6d08cfc39d7 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeInt.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeInt.java @@ -40,7 +40,7 @@ public MultivalueDedupeInt(IntBlock block) { * {@link Block} using an adaptive algorithm based on the size of the input list. */ public IntBlock dedupeToBlockAdaptive() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } IntBlock.Builder builder = IntBlock.newBlockBuilder(block.getPositionCount()); @@ -88,7 +88,7 @@ public IntBlock dedupeToBlockAdaptive() { * which picks based on the number of elements at each position. */ public IntBlock dedupeToBlockUsingCopyAndSort() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } IntBlock.Builder builder = IntBlock.newBlockBuilder(block.getPositionCount()); @@ -116,7 +116,7 @@ public IntBlock dedupeToBlockUsingCopyAndSort() { * {@link #dedupeToBlockAdaptive} unless you need the results sorted. */ public IntBlock dedupeToBlockUsingCopyMissing() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } IntBlock.Builder builder = IntBlock.newBlockBuilder(block.getPositionCount()); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeLong.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeLong.java index 0266131fba37c..d4da43f93d503 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeLong.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/MultivalueDedupeLong.java @@ -42,7 +42,7 @@ public MultivalueDedupeLong(LongBlock block) { * {@link Block} using an adaptive algorithm based on the size of the input list. */ public LongBlock dedupeToBlockAdaptive() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } LongBlock.Builder builder = LongBlock.newBlockBuilder(block.getPositionCount()); @@ -90,7 +90,7 @@ public LongBlock dedupeToBlockAdaptive() { * which picks based on the number of elements at each position. */ public LongBlock dedupeToBlockUsingCopyAndSort() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } LongBlock.Builder builder = LongBlock.newBlockBuilder(block.getPositionCount()); @@ -118,7 +118,7 @@ public LongBlock dedupeToBlockUsingCopyAndSort() { * {@link #dedupeToBlockAdaptive} unless you need the results sorted. */ public LongBlock dedupeToBlockUsingCopyMissing() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } LongBlock.Builder builder = LongBlock.newBlockBuilder(block.getPositionCount()); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBoolean.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBoolean.java index 0bdc5ac620eb0..40fe7ffdde661 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBoolean.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBoolean.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.operator.topn; -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -19,11 +18,11 @@ static KeyExtractorForBoolean extractorFor(TopNEncoder encoder, boolean ascendin return new KeyExtractorForBoolean.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForBoolean.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorForBoolean.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForBoolean.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorForBoolean.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBytesRef.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBytesRef.java index accce46f38e30..2f546a46aaeaf 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBytesRef.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForBytesRef.java @@ -8,7 +8,6 @@ package org.elasticsearch.compute.operator.topn; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -20,11 +19,11 @@ static KeyExtractorForBytesRef extractorFor(TopNEncoder encoder, boolean ascendi return new KeyExtractorForBytesRef.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForBytesRef.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorForBytesRef.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForBytesRef.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorForBytesRef.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForDouble.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForDouble.java index 2f2968da16d83..5e821b9e24db5 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForDouble.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForDouble.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.operator.topn; -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -19,11 +18,11 @@ static KeyExtractorForDouble extractorFor(TopNEncoder encoder, boolean ascending return new KeyExtractorForDouble.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForDouble.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorForDouble.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForDouble.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorForDouble.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForInt.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForInt.java index 400c43168277d..d4269a622f098 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForInt.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForInt.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.operator.topn; -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -19,11 +18,11 @@ static KeyExtractorForInt extractorFor(TopNEncoder encoder, boolean ascending, b return new KeyExtractorForInt.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForInt.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorForInt.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForInt.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorForInt.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForLong.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForLong.java index 843efdd95471f..6a200efff529d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForLong.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/operator/topn/KeyExtractorForLong.java @@ -7,7 +7,6 @@ package org.elasticsearch.compute.operator.topn; -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -19,11 +18,11 @@ static KeyExtractorForLong extractorFor(TopNEncoder encoder, boolean ascending, return new KeyExtractorForLong.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForLong.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorForLong.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorForLong.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorForLong.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java index 3a52beb9c2d87..f3ca16869898f 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java @@ -82,7 +82,8 @@ public DoubleBlock[] getKeys() { } BitSet nulls = new BitSet(1); nulls.set(0); - return new DoubleBlock[] { new DoubleArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.ASCENDING) }; + return new DoubleBlock[] { + new DoubleArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING) }; } final int size = Math.toIntExact(longHash.size()); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java index 4fcd9735f6158..08b2cd15aa53e 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java @@ -78,7 +78,7 @@ public IntBlock[] getKeys() { } BitSet nulls = new BitSet(1); nulls.set(0); - return new IntBlock[] { new IntArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.ASCENDING) }; + return new IntBlock[] { new IntArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING) }; } final int size = Math.toIntExact(longHash.size()); final int[] keys = new int[size]; diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java index 5e5b46ae6eda1..00e93db9cec00 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java @@ -82,7 +82,8 @@ public LongBlock[] getKeys() { } BitSet nulls = new BitSet(1); nulls.set(0); - return new LongBlock[] { new LongArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.ASCENDING) }; + return new LongBlock[] { + new LongArrayBlock(keys, keys.length, null, nulls, Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING) }; } final int size = Math.toIntExact(longHash.size()); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java index d83d26cf33831..4a019db5e03c0 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractVectorBlock.java @@ -52,7 +52,7 @@ public boolean mayHaveMultivaluedFields() { @Override public final MvOrdering mvOrdering() { - return MvOrdering.UNORDERED; + return MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index ca0721ebbe4f8..9c05d6d0ddfb8 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -102,12 +102,20 @@ public interface Block extends Accountable, NamedWriteable, Releasable { /** * How are multivalued fields ordered? - *

    Note that there isn't a {@code DESCENDING} because we don't have - * anything that makes descending fields.

    + * Some operators can enable its optimization when mv_values are sorted ascending or de-duplicated. */ enum MvOrdering { - ASCENDING, - UNORDERED; + UNORDERED(false, false), + DEDUPLICATED_UNORDERD(true, false), + DEDUPLICATED_AND_SORTED_ASCENDING(true, true); + + private final boolean deduplicated; + private final boolean sortedAscending; + + MvOrdering(boolean deduplicated, boolean sortedAscending) { + this.deduplicated = deduplicated; + this.sortedAscending = sortedAscending; + } } /** @@ -115,6 +123,20 @@ enum MvOrdering { */ MvOrdering mvOrdering(); + /** + * Are multivalued fields de-duplicated in each position + */ + default boolean mvDeduplicated() { + return mayHaveMultivaluedFields() == false || mvOrdering().deduplicated; + } + + /** + * Are multivalued fields sorted ascending in each position + */ + default boolean mvSortedAscending() { + return mayHaveMultivaluedFields() == false || mvOrdering().sortedAscending; + } + /** * Expand multivalued fields into one row per value. Returns the * block if there aren't any multivalued fields to expand. @@ -172,7 +194,7 @@ interface Builder extends Releasable { /** * How are multivalued fields ordered? This defaults to {@link Block.MvOrdering#UNORDERED} - * but when you set it to {@link Block.MvOrdering#ASCENDING} some operators can optimize + * but when you set it to {@link Block.MvOrdering#DEDUPLICATED_AND_SORTED_ASCENDING} some operators can optimize * themselves. This is a promise that is never checked. If you set this * to anything other than {@link Block.MvOrdering#UNORDERED} be sure the values are in * that order or other operators will make mistakes. The actual ordering isn't checked diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java index 0d09f7bb480e0..a41ea0383368d 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java @@ -8,10 +8,13 @@ package org.elasticsearch.compute.data; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Randomness; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.function.Consumer; import static org.elasticsearch.common.lucene.BytesRefs.toBytesRef; @@ -68,8 +71,13 @@ public static Block[] fromListRow(List row, int blockSize) { if (object instanceof List listVal) { BuilderWrapper wrapper = wrapperFor(fromJava(listVal.get(0).getClass()), blockSize); wrapper.accept(listVal); - if (isAscending(listVal)) { - wrapper.builder.mvOrdering(Block.MvOrdering.ASCENDING); + Random random = Randomness.get(); + if (isDeduplicated(listVal) && random.nextBoolean()) { + if (isAscending(listVal) && random.nextBoolean()) { + wrapper.builder.mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } else { + wrapper.builder.mvOrdering(Block.MvOrdering.DEDUPLICATED_UNORDERD); + } } blocks[i] = wrapper.builder.build(); } else { @@ -100,6 +108,14 @@ private static boolean isAscending(List values) { return true; } + /** + * Detect blocks with deduplicated fields. This is *mostly* useful for + * exercising the specialized ascending implementations. + */ + private static boolean isDeduplicated(List values) { + return new HashSet<>(values).size() == values.size(); + } + public static Block[] fromList(List> list) { var size = list.size(); if (size == 0) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java index 4290075b05ae8..28a9359497393 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java @@ -142,7 +142,7 @@ private static class LongSingletonValuesReader extends BlockDocValuesReader { @Override public LongBlock.Builder builder(int positionCount) { - return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -197,7 +197,7 @@ private static class LongValuesReader extends BlockDocValuesReader { @Override public LongBlock.Builder builder(int positionCount) { - return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -259,7 +259,7 @@ private static class IntSingletonValuesReader extends BlockDocValuesReader { @Override public IntBlock.Builder builder(int positionCount) { - return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -314,7 +314,7 @@ private static class IntValuesReader extends BlockDocValuesReader { @Override public IntBlock.Builder builder(int positionCount) { - return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -378,7 +378,7 @@ private static class DoubleSingletonValuesReader extends BlockDocValuesReader { @Override public DoubleBlock.Builder builder(int positionCount) { - return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -435,7 +435,7 @@ private static class DoubleValuesReader extends BlockDocValuesReader { @Override public DoubleBlock.Builder builder(int positionCount) { - return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -497,7 +497,7 @@ private static class BytesValuesReader extends BlockDocValuesReader { @Override public BytesRefBlock.Builder builder(int positionCount) { - return BytesRefBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return BytesRefBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -558,7 +558,7 @@ private static class BooleanSingletonValuesReader extends BlockDocValuesReader { @Override public BooleanBlock.Builder builder(int positionCount) { - return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override @@ -613,7 +613,7 @@ private static class BooleanValuesReader extends BlockDocValuesReader { @Override public BooleanBlock.Builder builder(int positionCount) { - return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.ASCENDING); + return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/X-MultivalueDedupe.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/X-MultivalueDedupe.java.st index 337b095ebe8d0..bd3e290a3625c 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/X-MultivalueDedupe.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/X-MultivalueDedupe.java.st @@ -70,7 +70,7 @@ $endif$ * {@link Block} using an adaptive algorithm based on the size of the input list. */ public $Type$Block dedupeToBlockAdaptive() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } $Type$Block.Builder builder = $Type$Block.newBlockBuilder(block.getPositionCount()); @@ -122,7 +122,7 @@ $endif$ * which picks based on the number of elements at each position. */ public $Type$Block dedupeToBlockUsingCopyAndSort() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } $Type$Block.Builder builder = $Type$Block.newBlockBuilder(block.getPositionCount()); @@ -154,7 +154,7 @@ $endif$ * {@link #dedupeToBlockAdaptive} unless you need the results sorted. */ public $Type$Block dedupeToBlockUsingCopyMissing() { - if (false == block.mayHaveMultivaluedFields()) { + if (block.mvDeduplicated()) { return block; } $Type$Block.Builder builder = $Type$Block.newBlockBuilder(block.getPositionCount()); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-KeyExtractor.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-KeyExtractor.java.st index 9ec03270da093..dbe0b23af93bb 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-KeyExtractor.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/topn/X-KeyExtractor.java.st @@ -10,7 +10,6 @@ package org.elasticsearch.compute.operator.topn; $if(BytesRef)$ import org.apache.lucene.util.BytesRef; $endif$ -import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.$Type$Block; import org.elasticsearch.compute.data.$Type$Vector; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; @@ -22,11 +21,11 @@ abstract class KeyExtractorFor$Type$ implements KeyExtractor { return new KeyExtractorFor$Type$.ForVector(encoder, nul, nonNul, v); } if (ascending) { - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorFor$Type$.MinForAscending(encoder, nul, nonNul, block) : new KeyExtractorFor$Type$.MinForUnordered(encoder, nul, nonNul, block); } - return block.mvOrdering() == Block.MvOrdering.ASCENDING + return block.mvSortedAscending() ? new KeyExtractorFor$Type$.MaxForAscending(encoder, nul, nonNul, block) : new KeyExtractorFor$Type$.MaxForUnordered(encoder, nul, nonNul, block); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java index 4776b40e12115..4c0e33e5cfb82 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java @@ -328,7 +328,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) assertThat(mvKeywords.getBytesRef(offset + v, new BytesRef()).utf8ToString(), equalTo(PREFIX[v] + key)); } if (key % 3 > 0) { - assertThat(mvKeywords.mvOrdering(), equalTo(Block.MvOrdering.ASCENDING)); + assertThat(mvKeywords.mvOrdering(), equalTo(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING)); } assertThat(bools.getBoolean(i), equalTo(key % 2 == 0)); @@ -338,7 +338,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) assertThat(mvBools.getBoolean(offset + v), equalTo(BOOLEANS[key % 3][v])); } if (key % 3 > 0) { - assertThat(mvBools.mvOrdering(), equalTo(Block.MvOrdering.ASCENDING)); + assertThat(mvBools.mvOrdering(), equalTo(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING)); } assertThat(mvInts.getValueCount(i), equalTo(key % 3 + 1)); @@ -347,7 +347,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) assertThat(mvInts.getInt(offset + v), equalTo(1_000 * key + v)); } if (key % 3 > 0) { - assertThat(mvInts.mvOrdering(), equalTo(Block.MvOrdering.ASCENDING)); + assertThat(mvInts.mvOrdering(), equalTo(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING)); } assertThat(mvLongs.getValueCount(i), equalTo(key % 3 + 1)); @@ -356,7 +356,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) assertThat(mvLongs.getLong(offset + v), equalTo(-1_000L * key + v)); } if (key % 3 > 0) { - assertThat(mvLongs.mvOrdering(), equalTo(Block.MvOrdering.ASCENDING)); + assertThat(mvLongs.mvOrdering(), equalTo(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING)); } assertThat(doubles.getDouble(i), equalTo(key / 123_456d)); @@ -365,7 +365,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) assertThat(mvDoubles.getDouble(offset + v), equalTo(key / 123_456d + v)); } if (key % 3 > 0) { - assertThat(mvDoubles.mvOrdering(), equalTo(Block.MvOrdering.ASCENDING)); + assertThat(mvDoubles.mvOrdering(), equalTo(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING)); } } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java index 95f6613d3c0a4..c331b7ab013ae 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java @@ -1118,7 +1118,7 @@ public void testIPSortingUnorderedMultiValues() throws UnknownHostException { public void testIPSortingOrderedMultiValues() throws UnknownHostException { List> ips = new ArrayList<>(); - ips.add(List.of("123.4.245.23", "123.4.245.23")); + ips.add(List.of("123.4.245.23", "123.4.245.24")); ips.add(null); ips.add(List.of("104.30.244.2", "127.0.0.1")); ips.add(null); @@ -1135,17 +1135,17 @@ public void testIPSortingOrderedMultiValues() throws UnknownHostException { expectedDecodedIps.add(List.of("104.30.244.2", "127.0.0.1")); expectedDecodedIps.add(List.of("104.30.244.2", "124.255.255.255")); expectedDecodedIps.add(List.of("104.244.4.1")); - expectedDecodedIps.add(List.of("123.4.245.23", "123.4.245.23")); + expectedDecodedIps.add(List.of("123.4.245.23", "123.4.245.24")); } else { expectedDecodedIps.add(List.of("1.198.3.93", "2.3.4.5", "255.123.123.0")); expectedDecodedIps.add(List.of("104.30.244.2", "127.0.0.1")); expectedDecodedIps.add(List.of("104.30.244.2", "124.255.255.255")); - expectedDecodedIps.add(List.of("123.4.245.23", "123.4.245.23")); + expectedDecodedIps.add(List.of("123.4.245.23", "123.4.245.24")); expectedDecodedIps.add(List.of("104.244.4.1")); expectedDecodedIps.add(List.of("1.1.1.0", "32.183.93.40")); } - assertIPSortingOnMultiValues(ips, asc, Block.MvOrdering.ASCENDING, expectedDecodedIps); + assertIPSortingOnMultiValues(ips, asc, Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING, expectedDecodedIps); } private void assertIPSortingOnMultiValues( diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java index 3b15fe9f17293..67ad08a101dab 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java @@ -36,7 +36,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } BooleanBlock v = (BooleanBlock) fieldVal; @@ -66,7 +66,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } BooleanBlock v = (BooleanBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java index 6401664c9aa0d..36eaecf36e345 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java @@ -37,7 +37,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } BytesRefBlock v = (BytesRefBlock) fieldVal; @@ -69,7 +69,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } BytesRefBlock v = (BytesRefBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java index 0ec72b82e2438..072dc413168ec 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } DoubleBlock v = (DoubleBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } DoubleBlock v = (DoubleBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java index 2bf14b26c6c5e..4d6a68ed67f26 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java index ce5a95bee7699..fd0ee6bf57740 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java index 711992a20763e..a84a4059b1dc0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java index 67d3c123a6953..4c5798bed2e35 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java @@ -36,7 +36,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; @@ -66,7 +66,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java index 93538708039b5..1731d0733b511 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java @@ -36,7 +36,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; @@ -66,7 +66,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java index 6e16c8db4b896..afb5e1cb7cda1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java @@ -36,7 +36,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } BooleanBlock v = (BooleanBlock) fieldVal; @@ -66,7 +66,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } BooleanBlock v = (BooleanBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java index 99a671cf0a2df..41b487553dc8e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java @@ -37,7 +37,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } BytesRefBlock v = (BytesRefBlock) fieldVal; @@ -69,7 +69,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } BytesRefBlock v = (BytesRefBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java index e40ff78d0d364..63da4bd86c673 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } DoubleBlock v = (DoubleBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } DoubleBlock v = (DoubleBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java index 9412930da53c5..46dedaed43a3d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } IntBlock v = (IntBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java index 1fac131f0de0c..8e17c8f08a906 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java @@ -35,7 +35,7 @@ public String name() { */ @Override public Block evalNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; @@ -65,7 +65,7 @@ public Block evalNullable(Block fieldVal) { */ @Override public Vector evalNotNullable(Block fieldVal) { - if (fieldVal.mvOrdering() == Block.MvOrdering.ASCENDING) { + if (fieldVal.mvSortedAscending()) { return evalAscendingNotNullable(fieldVal); } LongBlock v = (LongBlock) fieldVal; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java index 714112b2db543..cef65b6c477c5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java @@ -26,6 +26,8 @@ import java.math.BigInteger; import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.function.BiFunction; import java.util.stream.DoubleStream; @@ -385,7 +387,17 @@ private static > void putInOrder(List mvData, Block.M switch (ordering) { case UNORDERED -> { } - case ASCENDING -> Collections.sort(mvData); + case DEDUPLICATED_UNORDERD -> { + var dedup = new LinkedHashSet<>(mvData); + mvData.clear(); + mvData.addAll(dedup); + } + case DEDUPLICATED_AND_SORTED_ASCENDING -> { + var dedup = new HashSet<>(mvData); + mvData.clear(); + mvData.addAll(dedup); + Collections.sort(mvData); + } default -> throw new UnsupportedOperationException("unsupported ordering [" + ordering + "]"); } } From 8f3d374d6dd58a220d5ee8f1f94c09d5d7e9dcb7 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Thu, 28 Sep 2023 16:05:12 -0400 Subject: [PATCH 139/155] [DOCS] Add security update to 8.9.2 release notes (#99949) Adds an update for the [audit logs security announcement](https://discuss.elastic.co/t/elasticsearch-8-9-2-and-7-17-13-security-update/342479) to the 8.9.2 release notes. This was overlooked when the announcement was first made. I plan to add a similar note to the 7.17.13 release notes. --- docs/reference/release-notes/8.9.2.asciidoc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/reference/release-notes/8.9.2.asciidoc b/docs/reference/release-notes/8.9.2.asciidoc index d4244eab27645..6b00405261daf 100644 --- a/docs/reference/release-notes/8.9.2.asciidoc +++ b/docs/reference/release-notes/8.9.2.asciidoc @@ -3,6 +3,25 @@ Also see <>. +[float] +[[security-updates-8.9.2]] +=== Security updates + +* {es} generally filters out sensitive information and credentials before +logging to the audit log. It was found that this filtering was not applied when +requests to {es} use certain deprecated `_xpack/security` URIs for APIs. The +impact of this flaw is that sensitive information, such as passwords and tokens, +might be printed in cleartext in {es} audit logs. Note that audit logging is +disabled by default and needs to be explicitly enabled. Even when audit logging +is enabled, request bodies that could contain sensitive information are not +printed to the audit log unless explicitly configured. ++ +The issue is resolved in {es} 8.9.2. ++ +For more information, see our related +https://discuss.elastic.co/t/elasticsearch-8-9-2-and-7-17-13-security-update/342479[security +announcement]. + [[bug-8.9.2]] [float] === Bug fixes From f99a80f42d4c5eff8a539ffca99a865e9e2dffe6 Mon Sep 17 00:00:00 2001 From: Chris Cressman Date: Thu, 28 Sep 2023 17:32:37 -0400 Subject: [PATCH 140/155] [DOCS] Create URLs for docs migrating from Enterprise Search (#100032) Several docs are going to migrate from Enterprise Search to Elasticsearch. Create the new URLs without yet placing the pages into the Elasticsearch navigation. This will allow us to handle redirects for Kibana, docs, and the web without waiting for additional content design decisions. The new URLs will be: - https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-pipeline-search.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-pipeline-search-inference.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-pipeline-search-inference.html#ingest-pipeline-search-inference-update-mapping - https://www.elastic.co/guide/en/elasticsearch/reference/master/nlp-example.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-overview.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-start.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-api.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-event.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-event-reference.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/behavioral-analytics-cors.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/search-application-overview.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/search-application-api.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/search-application-client.html - https://www.elastic.co/guide/en/elasticsearch/reference/master/search-application-security.html --- docs/reference/redirects.asciidoc | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/reference/redirects.asciidoc b/docs/reference/redirects.asciidoc index 8e1023c47b929..4ec8c203bbef9 100644 --- a/docs/reference/redirects.asciidoc +++ b/docs/reference/redirects.asciidoc @@ -1932,3 +1932,73 @@ Refer to <>. === Configure roles and users for remote clusters Refer to <>. + +[role="exclude",id="ingest-pipeline-search"] +=== Ingest pipelines for Search indices + +coming::[8.11.0] + +[role="exclude",id="ingest-pipeline-search-inference"] +=== Inference processing for Search indices + +coming::[8.11.0] + +[id="ingest-pipeline-search-inference-update-mapping"] +==== Update mapping + +coming::[8.11.0] + +[role="exclude",id="nlp-example"] +=== Tutorial: Natural language processing (NLP) + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-overview"] +=== Elastic Behavioral Analytics + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-start"] +=== Get started with Behavioral Analytics + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-api"] +=== Behavioral Analytics APIs + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-event"] +=== View Behavioral Analytics Events + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-event-reference"] +=== Behavioral Analytics events reference + +coming::[8.11.0] + +[role="exclude",id="behavioral-analytics-cors"] +=== Set up CORS for Behavioral Analytics + +coming::[8.11.0] + +[role="exclude",id="search-application-overview"] +=== Elastic Search Applications + +coming::[8.11.0] + +[role="exclude",id="search-application-api"] +=== Search Applications search API and templates + +coming::[8.11.0] + +[role="exclude",id="search-application-client"] +=== Search Applications client + +coming::[8.11.0] + +[role="exclude",id="search-application-security"] +=== Search Applications security + +coming::[8.11.0] From f8d09e9c6cf4b611de3dc7509b0aab6e6a1d5d7a Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Thu, 28 Sep 2023 19:35:46 -0500 Subject: [PATCH 141/155] APM Metering API (#99832) Adds Metering instrument interfaces and adapter implementations for opentelemetry instrument types: * Gauge - a single number that can go up or down * Histogram - bucketed samples * Counter - monotonically increasing summed value * UpDownCounter - summed value that may decrease Supports both Long* and Double* versions of the instruments. Instruments can be registered and retrieved by name through APMMeter which is available via the APMTelemetryProvider. The metering provider starts as the open telemetry noop provider. `telemetry.metrics.enabled` turns on metering. --- docs/changelog/99832.yaml | 5 + .../org/elasticsearch/telemetry/apm/APM.java | 6 +- .../apm/internal/APMAgentSettings.java | 26 +- .../apm/internal/APMTelemetryProvider.java | 8 + .../apm/internal/metrics/APMMeter.java | 180 ++++++++++++++ .../internal/metrics/AbstractInstrument.java | 66 +++++ .../metrics/DoubleCounterAdapter.java | 52 ++++ .../internal/metrics/DoubleGaugeAdapter.java | 42 ++++ .../metrics/DoubleHistogramAdapter.java | 42 ++++ .../metrics/DoubleUpDownCounterAdapter.java | 46 ++++ .../apm/internal/metrics/Instruments.java | 184 ++++++++++++++ .../internal/metrics/LongCounterAdapter.java | 49 ++++ .../internal/metrics/LongGaugeAdapter.java | 46 ++++ .../metrics/LongHistogramAdapter.java | 46 ++++ .../metrics/LongUpDownCounterAdapter.java | 42 ++++ .../apm/internal/metrics/OtelHelper.java | 36 +++ .../apm/internal/tracing/APMTracer.java | 2 +- .../plugin-metadata/plugin-security.policy | 2 + .../apm/internal/metrics/APMMeterTests.java | 85 +++++++ .../metrics/InstrumentsConcurrencyTests.java | 112 +++++++++ .../internal/metrics/InstrumentsTests.java | 77 ++++++ server/src/main/java/module-info.java | 1 + .../telemetry/TelemetryProvider.java | 8 + .../telemetry/metric/DoubleCounter.java | 60 +++++ .../telemetry/metric/DoubleGauge.java | 47 ++++ .../telemetry/metric/DoubleHistogram.java | 48 ++++ .../telemetry/metric/DoubleUpDownCounter.java | 50 ++++ .../telemetry/metric/Instrument.java | 13 + .../telemetry/metric/LongCounter.java | 60 +++++ .../telemetry/metric/LongGauge.java | 49 ++++ .../telemetry/metric/LongHistogram.java | 48 ++++ .../telemetry/metric/LongUpDownCounter.java | 50 ++++ .../elasticsearch/telemetry/metric/Meter.java | 228 ++++++++++++++++++ 33 files changed, 1811 insertions(+), 5 deletions(-) create mode 100644 docs/changelog/99832.yaml create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java create mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java create mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java create mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/DoubleCounter.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/DoubleHistogram.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/DoubleUpDownCounter.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/Instrument.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/LongCounter.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/LongHistogram.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/LongUpDownCounter.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java diff --git a/docs/changelog/99832.yaml b/docs/changelog/99832.yaml new file mode 100644 index 0000000000000..9bd83591ba920 --- /dev/null +++ b/docs/changelog/99832.yaml @@ -0,0 +1,5 @@ +pr: 99832 +summary: APM Metering API +area: Infra/Core +type: enhancement +issues: [] diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java index be59eda4a63c2..935c4958ba3d7 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java @@ -27,6 +27,7 @@ import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider; +import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter; import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -97,13 +98,16 @@ public Collection createComponents( apmAgentSettings.syncAgentSystemProperties(settings); apmAgentSettings.addClusterSettingsListeners(clusterService, telemetryProvider.get()); - return List.of(apmTracer); + final APMMeter apmMeter = telemetryProvider.get().getMeter(); + + return List.of(apmTracer, apmMeter); } @Override public List> getSettings() { return List.of( APMAgentSettings.APM_ENABLED_SETTING, + APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING, APMAgentSettings.APM_TRACING_NAMES_INCLUDE_SETTING, APMAgentSettings.APM_TRACING_NAMES_EXCLUDE_SETTING, APMAgentSettings.APM_TRACING_SANITIZE_FIELD_NAMES, diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java index 75ca94bb13ad6..e4a194ebe0172 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter; import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; import java.security.AccessController; @@ -40,14 +41,24 @@ public class APMAgentSettings { * Sensible defaults that Elasticsearch configures. This cannot be done via the APM agent * config file, as then their values could not be overridden dynamically via system properties. */ - static Map APM_AGENT_DEFAULT_SETTINGS = Map.of("transaction_sample_rate", "0.2"); + static Map APM_AGENT_DEFAULT_SETTINGS = Map.of( + "transaction_sample_rate", + "0.2", + "enable_experimental_instrumentations", + "true" + ); public void addClusterSettingsListeners(ClusterService clusterService, APMTelemetryProvider apmTelemetryProvider) { final ClusterSettings clusterSettings = clusterService.getClusterSettings(); final APMTracer apmTracer = apmTelemetryProvider.getTracer(); + final APMMeter apmMeter = apmTelemetryProvider.getMeter(); clusterSettings.addSettingsUpdateConsumer(APM_ENABLED_SETTING, enabled -> { apmTracer.setEnabled(enabled); + this.setAgentSetting("instrument", Boolean.toString(enabled)); + }); + clusterSettings.addSettingsUpdateConsumer(TELEMETRY_METRICS_ENABLED_SETTING, enabled -> { + apmMeter.setEnabled(enabled); // The agent records data other than spans, e.g. JVM metrics, so we toggle this setting in order to // minimise its impact to a running Elasticsearch. this.setAgentSetting("recording", Boolean.toString(enabled)); @@ -106,8 +117,10 @@ public void setAgentSetting(String key, String value) { private static final List PROHIBITED_AGENT_KEYS = List.of( // ES generates a config file and sets this value "config_file", - // ES controls this via `tracing.apm.enabled` - "recording" + // ES controls this via `telemetry.metrics.enabled` + "recording", + // ES controls this via `apm.enabled` + "instrument" ); public static final Setting.AffixSetting APM_AGENT_SETTINGS = Setting.prefixKeySetting( @@ -164,6 +177,13 @@ public void setAgentSetting(String key, String value) { NodeScope ); + public static final Setting TELEMETRY_METRICS_ENABLED_SETTING = Setting.boolSetting( + "telemetry.metrics.enabled", + false, + OperatorDynamic, + NodeScope + ); + public static final Setting APM_SECRET_TOKEN_SETTING = SecureSetting.secureString( APM_SETTING_PREFIX + "secret_token", null diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java index 495afd43bf176..ae9d91cc6ec51 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java @@ -10,19 +10,27 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.telemetry.TelemetryProvider; +import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter; import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; public class APMTelemetryProvider implements TelemetryProvider { private final Settings settings; private final APMTracer apmTracer; + private final APMMeter apmMeter; public APMTelemetryProvider(Settings settings) { this.settings = settings; apmTracer = new APMTracer(settings); + apmMeter = new APMMeter(settings); } @Override public APMTracer getTracer() { return apmTracer; } + + @Override + public APMMeter getMeter() { + return apmMeter; + } } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java new file mode 100644 index 0000000000000..0a8d425579ca2 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; + +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider; +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleHistogram; +import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.LongCounter; +import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongHistogram; +import org.elasticsearch.telemetry.metric.LongUpDownCounter; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.function.Supplier; + +import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING; + +public class APMMeter extends AbstractLifecycleComponent implements org.elasticsearch.telemetry.metric.Meter { + private final Instruments instruments; + + private final Supplier otelMeterSupplier; + private final Supplier noopMeterSupplier; + + private volatile boolean enabled; + + public APMMeter(Settings settings) { + this(settings, APMMeter.otelMeter(), APMMeter.noopMeter()); + } + + public APMMeter(Settings settings, Supplier otelMeterSupplier, Supplier noopMeterSupplier) { + this.enabled = TELEMETRY_METRICS_ENABLED_SETTING.get(settings); + this.otelMeterSupplier = otelMeterSupplier; + this.noopMeterSupplier = noopMeterSupplier; + this.instruments = new Instruments(enabled ? createOtelMeter() : createNoopMeter()); + } + + /** + * @see org.elasticsearch.telemetry.apm.internal.APMAgentSettings#addClusterSettingsListeners(ClusterService, APMTelemetryProvider) + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + if (enabled) { + instruments.setProvider(createOtelMeter()); + } else { + instruments.setProvider(createNoopMeter()); + } + } + + @Override + protected void doStart() {} + + @Override + protected void doStop() { + instruments.setProvider(createNoopMeter()); + } + + @Override + protected void doClose() {} + + @Override + public DoubleCounter registerDoubleCounter(String name, String description, String unit) { + return instruments.registerDoubleCounter(name, description, unit); + } + + @Override + public DoubleCounter getDoubleCounter(String name) { + return instruments.getDoubleCounter(name); + } + + @Override + public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { + return instruments.registerDoubleUpDownCounter(name, description, unit); + } + + @Override + public DoubleUpDownCounter getDoubleUpDownCounter(String name) { + return instruments.getDoubleUpDownCounter(name); + } + + @Override + public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + return instruments.registerDoubleGauge(name, description, unit); + } + + @Override + public DoubleGauge getDoubleGauge(String name) { + return instruments.getDoubleGauge(name); + } + + @Override + public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { + return instruments.registerDoubleHistogram(name, description, unit); + } + + @Override + public DoubleHistogram getDoubleHistogram(String name) { + return instruments.getDoubleHistogram(name); + } + + @Override + public LongCounter registerLongCounter(String name, String description, String unit) { + return instruments.registerLongCounter(name, description, unit); + } + + @Override + public LongCounter getLongCounter(String name) { + return instruments.getLongCounter(name); + } + + @Override + public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { + return instruments.registerLongUpDownCounter(name, description, unit); + } + + @Override + public LongUpDownCounter getLongUpDownCounter(String name) { + return instruments.getLongUpDownCounter(name); + } + + @Override + public LongGauge registerLongGauge(String name, String description, String unit) { + return instruments.registerLongGauge(name, description, unit); + } + + @Override + public LongGauge getLongGauge(String name) { + return instruments.getLongGauge(name); + } + + @Override + public LongHistogram registerLongHistogram(String name, String description, String unit) { + return instruments.registerLongHistogram(name, description, unit); + } + + @Override + public LongHistogram getLongHistogram(String name) { + return instruments.getLongHistogram(name); + } + + Meter createOtelMeter() { + assert this.enabled; + return AccessController.doPrivileged((PrivilegedAction) otelMeterSupplier::get); + } + + private Meter createNoopMeter() { + return noopMeterSupplier.get(); + } + + private static Supplier noopMeter() { + return () -> OpenTelemetry.noop().getMeter("noop"); + } + + // to be used within doPrivileged block + private static Supplier otelMeter() { + var openTelemetry = GlobalOpenTelemetry.get(); + var meter = openTelemetry.getMeter("elasticsearch"); + return () -> meter; + } + + // scope for testing + Instruments getInstruments() { + return instruments; + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java new file mode 100644 index 0000000000000..d3d485f52bc49 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import org.elasticsearch.core.Nullable; +import org.elasticsearch.telemetry.metric.Instrument; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * An instrument that contains the name, description and unit. The delegate may be replaced when + * the provider is updated. + * Subclasses should implement the builder, which is used on initialization and provider updates. + * @param delegated instrument + */ +public abstract class AbstractInstrument implements Instrument { + private final AtomicReference delegate; + private final String name; + private final String description; + private final String unit; + + public AbstractInstrument(Meter meter, String name, String description, String unit) { + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + this.unit = Objects.requireNonNull(unit); + this.delegate = new AtomicReference<>(doBuildInstrument(meter)); + } + + private T doBuildInstrument(Meter meter) { + return AccessController.doPrivileged((PrivilegedAction) () -> buildInstrument(meter)); + } + + @Override + public String getName() { + return name; + } + + public String getUnit() { + return unit.toString(); + } + + T getInstrument() { + return delegate.get(); + } + + String getDescription() { + return description; + } + + void setProvider(@Nullable Meter meter) { + delegate.set(doBuildInstrument(Objects.requireNonNull(meter))); + } + + abstract T buildInstrument(Meter meter); +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java new file mode 100644 index 0000000000000..b25ffdff5481b --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * DoubleGaugeAdapter wraps an otel ObservableDoubleMeasurement + */ +public class DoubleCounterAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.DoubleCounter { + + public DoubleCounterAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + io.opentelemetry.api.metrics.DoubleCounter buildInstrument(Meter meter) { + return Objects.requireNonNull(meter) + .counterBuilder(getName()) + .ofDoubles() + .setDescription(getDescription()) + .setUnit(getUnit()) + .build(); + } + + @Override + public void increment() { + getInstrument().add(1d); + } + + @Override + public void incrementBy(double inc) { + assert inc >= 0; + getInstrument().add(inc); + } + + @Override + public void incrementBy(double inc, Map attributes) { + assert inc >= 0; + getInstrument().add(inc, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java new file mode 100644 index 0000000000000..9d55d475d4a93 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * DoubleGaugeAdapter wraps an otel ObservableDoubleMeasurement + */ +public class DoubleGaugeAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.DoubleGauge { + + public DoubleGaugeAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.ObservableDoubleMeasurement buildInstrument(Meter meter) { + var builder = Objects.requireNonNull(meter).gaugeBuilder(getName()); + return builder.setDescription(getDescription()).setUnit(getUnit()).buildObserver(); + } + + @Override + public void record(double value) { + getInstrument().record(value); + } + + @Override + public void record(double value, Map attributes) { + getInstrument().record(value, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java new file mode 100644 index 0000000000000..5fd1a8a189b0f --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * DoubleHistogramAdapter wraps an otel DoubleHistogram + */ +public class DoubleHistogramAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.DoubleHistogram { + + public DoubleHistogramAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.DoubleHistogram buildInstrument(Meter meter) { + var builder = Objects.requireNonNull(meter).histogramBuilder(getName()); + return builder.setDescription(getDescription()).setUnit(getUnit()).build(); + } + + @Override + public void record(double value) { + getInstrument().record(value); + } + + @Override + public void record(double value, Map attributes) { + getInstrument().record(value, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java new file mode 100644 index 0000000000000..9a2fc1b564766 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * DoubleUpDownCounterAdapter wraps an otel DoubleUpDownCounter + */ +public class DoubleUpDownCounterAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.DoubleUpDownCounter { + + public DoubleUpDownCounterAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.DoubleUpDownCounter buildInstrument(Meter meter) { + return Objects.requireNonNull(meter) + .upDownCounterBuilder(getName()) + .ofDoubles() + .setDescription(getDescription()) + .setUnit(getUnit()) + .build(); + } + + @Override + public void add(double inc) { + getInstrument().add(inc); + } + + @Override + public void add(double inc, Map attributes) { + getInstrument().add(inc, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java new file mode 100644 index 0000000000000..92d7d692f0ea5 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.ReleasableLock; +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleHistogram; +import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.LongCounter; +import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongHistogram; +import org.elasticsearch.telemetry.metric.LongUpDownCounter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Container for registering and fetching instruments by type and name. + * Instrument names must be unique for a given type on registration. + * {@link #setProvider(Meter)} is used to change the provider for all existing instruments. + */ +public class Instruments { + private final Registrar doubleCounters = new Registrar<>(); + private final Registrar doubleUpDownCounters = new Registrar<>(); + private final Registrar doubleGauges = new Registrar<>(); + private final Registrar doubleHistograms = new Registrar<>(); + private final Registrar longCounters = new Registrar<>(); + private final Registrar longUpDownCounters = new Registrar<>(); + private final Registrar longGauges = new Registrar<>(); + private final Registrar longHistograms = new Registrar<>(); + + private final Meter meter; + + public Instruments(Meter meter) { + this.meter = meter; + } + + private final List> registrars = List.of( + doubleCounters, + doubleUpDownCounters, + doubleGauges, + doubleHistograms, + longCounters, + longUpDownCounters, + longGauges, + longHistograms + ); + + // Access to registration has to be restricted when the provider is updated in ::setProvider + protected final ReleasableLock registerLock = new ReleasableLock(new ReentrantLock()); + + public DoubleCounter registerDoubleCounter(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return doubleCounters.register(new DoubleCounterAdapter(meter, name, description, unit)); + } + } + + public DoubleCounter getDoubleCounter(String name) { + return doubleCounters.get(name); + } + + public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return doubleUpDownCounters.register(new DoubleUpDownCounterAdapter(meter, name, description, unit)); + } + } + + public DoubleUpDownCounter getDoubleUpDownCounter(String name) { + return doubleUpDownCounters.get(name); + } + + public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return doubleGauges.register(new DoubleGaugeAdapter(meter, name, description, unit)); + } + } + + public DoubleGauge getDoubleGauge(String name) { + return doubleGauges.get(name); + } + + public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return doubleHistograms.register(new DoubleHistogramAdapter(meter, name, description, unit)); + } + } + + public DoubleHistogram getDoubleHistogram(String name) { + return doubleHistograms.get(name); + } + + public LongCounter registerLongCounter(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return longCounters.register(new LongCounterAdapter(meter, name, description, unit)); + } + } + + public LongCounter getLongCounter(String name) { + return longCounters.get(name); + } + + public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return longUpDownCounters.register(new LongUpDownCounterAdapter(meter, name, description, unit)); + } + } + + public LongUpDownCounter getLongUpDownCounter(String name) { + return longUpDownCounters.get(name); + } + + public LongGauge registerLongGauge(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return longGauges.register(new LongGaugeAdapter(meter, name, description, unit)); + } + } + + public LongGauge getLongGauge(String name) { + return longGauges.get(name); + } + + public LongHistogram registerLongHistogram(String name, String description, String unit) { + try (ReleasableLock lock = registerLock.acquire()) { + return longHistograms.register(new LongHistogramAdapter(meter, name, description, unit)); + } + } + + public LongHistogram getLongHistogram(String name) { + return longHistograms.get(name); + } + + public void setProvider(Meter meter) { + try (ReleasableLock lock = registerLock.acquire()) { + for (Registrar registrar : registrars) { + registrar.setProvider(meter); + } + } + } + + /** + * A typed wrapper for a instrument that + * @param + */ + private static class Registrar> { + private final Map registered = ConcurrentCollections.newConcurrentMap(); + + T register(T instrument) { + registered.compute(instrument.getName(), (k, v) -> { + if (v != null) { + throw new IllegalStateException( + instrument.getClass().getSimpleName() + "[" + instrument.getName() + "] already registered" + ); + } + + return instrument; + }); + return instrument; + } + + T get(String name) { + return registered.get(name); + } + + void setProvider(Meter meter) { + registered.forEach((k, v) -> v.setProvider(meter)); + } + } + + // scope for testing + Meter getMeter() { + return meter; + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java new file mode 100644 index 0000000000000..122d16d9e1aa4 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * LongCounterAdapter wraps an otel LongCounter + */ +public class LongCounterAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.LongCounter { + + public LongCounterAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.LongCounter buildInstrument(Meter meter) { + var builder = Objects.requireNonNull(meter).counterBuilder(getName()); + return builder.setDescription(getDescription()).setUnit(getUnit()).build(); + } + + @Override + public void increment() { + getInstrument().add(1L); + } + + @Override + public void incrementBy(long inc) { + assert inc >= 0; + getInstrument().add(inc); + } + + @Override + public void incrementBy(long inc, Map attributes) { + assert inc >= 0; + getInstrument().add(inc, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java new file mode 100644 index 0000000000000..48430285a5173 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * LongGaugeAdapter wraps an otel ObservableLongMeasurement + */ +public class LongGaugeAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.LongGauge { + + public LongGaugeAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.ObservableLongMeasurement buildInstrument(Meter meter) { + return Objects.requireNonNull(meter) + .gaugeBuilder(getName()) + .ofLongs() + .setDescription(getDescription()) + .setUnit(getUnit()) + .buildObserver(); + } + + @Override + public void record(long value) { + getInstrument().record(value); + } + + @Override + public void record(long value, Map attributes) { + getInstrument().record(value, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java new file mode 100644 index 0000000000000..bb5be4866e7b7 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * LongHistogramAdapter wraps an otel LongHistogram + */ +public class LongHistogramAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.LongHistogram { + + public LongHistogramAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.LongHistogram buildInstrument(Meter meter) { + return Objects.requireNonNull(meter) + .histogramBuilder(getName()) + .ofLongs() + .setDescription(getDescription()) + .setUnit(getUnit()) + .build(); + } + + @Override + public void record(long value) { + getInstrument().record(value); + } + + @Override + public void record(long value, Map attributes) { + getInstrument().record(value, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java new file mode 100644 index 0000000000000..e5af85e4ed192 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.metrics.Meter; + +import java.util.Map; +import java.util.Objects; + +/** + * LongUpDownCounterAdapter wraps an otel LongUpDownCounter + */ +public class LongUpDownCounterAdapter extends AbstractInstrument + implements + org.elasticsearch.telemetry.metric.LongUpDownCounter { + + public LongUpDownCounterAdapter(Meter meter, String name, String description, String unit) { + super(meter, name, description, unit); + } + + @Override + io.opentelemetry.api.metrics.LongUpDownCounter buildInstrument(Meter meter) { + var builder = Objects.requireNonNull(meter).upDownCounterBuilder(getName()); + return builder.setDescription(getDescription()).setUnit(getUnit()).build(); + } + + @Override + public void add(long inc) { + getInstrument().add(inc); + } + + @Override + public void add(long inc, Map attributes) { + getInstrument().add(inc, OtelHelper.fromMap(attributes)); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java new file mode 100644 index 0000000000000..673025a1a41f4 --- /dev/null +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.common.Attributes; + +import java.util.Map; + +class OtelHelper { + static Attributes fromMap(Map attributes) { + if (attributes == null || attributes.isEmpty()) { + return Attributes.empty(); + } + var builder = Attributes.builder(); + attributes.forEach((k, v) -> { + if (v instanceof String value) { + builder.put(k, value); + } else if (v instanceof Long value) { + builder.put(k, value); + } else if (v instanceof Double value) { + builder.put(k, value); + } else if (v instanceof Boolean value) { + builder.put(k, value); + } else { + throw new IllegalArgumentException("attributes do not support value type of [" + v.getClass().getCanonicalName() + "]"); + } + }); + return builder.build(); + } +} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java index daedb90047975..9f9fdb3dc26ef 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java @@ -150,7 +150,6 @@ APMServices createApmServices() { return AccessController.doPrivileged((PrivilegedAction) () -> { var openTelemetry = GlobalOpenTelemetry.get(); var tracer = openTelemetry.getTracer("elasticsearch", Version.CURRENT.toString()); - return new APMServices(tracer, openTelemetry); }); } @@ -452,4 +451,5 @@ private static Automaton patternsToAutomaton(List patterns) { } return Operations.union(automata); } + } diff --git a/modules/apm/src/main/plugin-metadata/plugin-security.policy b/modules/apm/src/main/plugin-metadata/plugin-security.policy index b85d3ec05c277..57da3a2efd301 100644 --- a/modules/apm/src/main/plugin-metadata/plugin-security.policy +++ b/modules/apm/src/main/plugin-metadata/plugin-security.policy @@ -11,6 +11,8 @@ grant { permission java.lang.RuntimePermission "createClassLoader"; permission java.lang.RuntimePermission "getClassLoader"; permission java.util.PropertyPermission "elastic.apm.*", "write"; + permission java.util.PropertyPermission "*", "read,write"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; }; grant codeBase "${codebase.elastic-apm-agent}" { diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java new file mode 100644 index 0000000000000..1064b8820b089 --- /dev/null +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.sameInstance; + +public class APMMeterTests extends ESTestCase { + Meter testOtel = OpenTelemetry.noop().getMeter("test"); + + Meter noopOtel = OpenTelemetry.noop().getMeter("noop"); + + public void testMeterIsSetUponConstruction() { + // test default + APMMeter apmMeter = new APMMeter(Settings.EMPTY, () -> testOtel, () -> noopOtel); + + Meter meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(noopOtel)); + + // test explicitly enabled + var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build(); + apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel); + + meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(testOtel)); + + // test explicitly disabled + settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build(); + apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel); + + meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(noopOtel)); + } + + public void testMeterIsOverridden() { + APMMeter apmMeter = new APMMeter(Settings.EMPTY, () -> testOtel, () -> noopOtel); + + Meter meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(noopOtel)); + + apmMeter.setEnabled(true); + + meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(testOtel)); + } + + public void testLookupByName() { + var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build(); + + var apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel); + + DoubleCounter registeredCounter = apmMeter.registerDoubleCounter("name", "desc", "unit"); + DoubleCounter lookedUpCounter = apmMeter.getDoubleCounter("name"); + + assertThat(lookedUpCounter, sameInstance(registeredCounter)); + } + + public void testNoopIsSetOnStop() { + var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build(); + APMMeter apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel); + apmMeter.start(); + + Meter meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(testOtel)); + + apmMeter.stop(); + + meter = apmMeter.getInstruments().getMeter(); + assertThat(meter, sameInstance(noopOtel)); + } + +} diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java new file mode 100644 index 0000000000000..51285894f27ee --- /dev/null +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.DoubleCounterBuilder; +import io.opentelemetry.api.metrics.DoubleGaugeBuilder; +import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongCounterBuilder; +import io.opentelemetry.api.metrics.LongUpDownCounterBuilder; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableLongCounter; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; + +import org.elasticsearch.test.ESTestCase; + +import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.sameInstance; + +public class InstrumentsConcurrencyTests extends ESTestCase { + String name = "name"; + String description = "desc"; + String unit = "kg"; + Meter noopMeter = OpenTelemetry.noop().getMeter("noop"); + CountDownLatch registerLatch = new CountDownLatch(1); + Meter lockingMeter = new Meter() { + @Override + public LongCounterBuilder counterBuilder(String name) { + return new LockingLongCounterBuilder(); + } + + @Override + public LongUpDownCounterBuilder upDownCounterBuilder(String name) { + return null; + } + + @Override + public DoubleHistogramBuilder histogramBuilder(String name) { + return null; + } + + @Override + public DoubleGaugeBuilder gaugeBuilder(String name) { + return null; + } + }; + + class LockingLongCounterBuilder implements LongCounterBuilder { + + @Override + public LongCounterBuilder setDescription(String description) { + return this; + } + + @Override + public LongCounterBuilder setUnit(String unit) { + return this; + } + + @Override + public DoubleCounterBuilder ofDoubles() { + return null; + } + + @Override + public LongCounter build() { + try { + registerLatch.await(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public ObservableLongCounter buildWithCallback(Consumer callback) { + return null; + } + } + + public void testLockingWhenRegistering() throws Exception { + Instruments instruments = new Instruments(lockingMeter); + + var registerThread = new Thread(() -> instruments.registerLongCounter(name, description, unit)); + // registerThread has a countDown latch that is simulating a long-running registration + registerThread.start(); + var setProviderThread = new Thread(() -> instruments.setProvider(noopMeter)); + // a setProviderThread will attempt to override a meter, but will wait to acquireLock + setProviderThread.start(); + + // assert that a thread is waiting for a lock during long-running registration + assertBusy(() -> assertThat(setProviderThread.getState(), equalTo(Thread.State.WAITING))); + // assert that the old lockingMeter is still in place + assertBusy(() -> assertThat(instruments.getMeter(), sameInstance(lockingMeter))); + + // finish long-running registration + registerLatch.countDown(); + // assert that a meter was overriden + assertBusy(() -> assertThat(instruments.getMeter(), sameInstance(lockingMeter))); + + } +} diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java new file mode 100644 index 0000000000000..daf511fcf7042 --- /dev/null +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm.internal.metrics; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; + +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.sameInstance; + +public class InstrumentsTests extends ESTestCase { + Meter noopMeter = OpenTelemetry.noop().getMeter("noop"); + Meter someOtherMeter = OpenTelemetry.noop().getMeter("xyz"); + String name = "name"; + String description = "desc"; + String unit = "kg"; + + public void testRegistrationAndLookup() { + Instruments instruments = new Instruments(noopMeter); + { + var registered = instruments.registerDoubleCounter(name, description, unit); + var lookedUp = instruments.getDoubleCounter(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerDoubleUpDownCounter(name, description, unit); + var lookedUp = instruments.getDoubleUpDownCounter(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerDoubleGauge(name, description, unit); + var lookedUp = instruments.getDoubleGauge(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerDoubleHistogram(name, description, unit); + var lookedUp = instruments.getDoubleHistogram(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerLongCounter(name, description, unit); + var lookedUp = instruments.getLongCounter(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerLongUpDownCounter(name, description, unit); + var lookedUp = instruments.getLongUpDownCounter(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerLongGauge(name, description, unit); + var lookedUp = instruments.getLongGauge(name); + assertThat(registered, sameInstance(lookedUp)); + } + { + var registered = instruments.registerLongHistogram(name, description, unit); + var lookedUp = instruments.getLongHistogram(name); + assertThat(registered, sameInstance(lookedUp)); + } + } + + public void testNameValidation() { + Instruments instruments = new Instruments(noopMeter); + + instruments.registerLongHistogram(name, description, unit); + var e = expectThrows(IllegalStateException.class, () -> instruments.registerLongHistogram(name, description, unit)); + assertThat(e.getMessage(), equalTo("LongHistogramAdapter[name] already registered")); + } +} diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 99ce5910c9775..1a082e7558577 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -385,6 +385,7 @@ org.elasticsearch.serverless.apifiltering; exports org.elasticsearch.telemetry.tracing; exports org.elasticsearch.telemetry; + exports org.elasticsearch.telemetry.metric; provides java.util.spi.CalendarDataProvider with org.elasticsearch.common.time.IsoCalendarDataProvider; provides org.elasticsearch.xcontent.ErrorOnUnknown with org.elasticsearch.common.xcontent.SuggestingErrorOnUnknown; diff --git a/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java index 0df8aeedac7f8..add994787227f 100644 --- a/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java +++ b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java @@ -8,11 +8,15 @@ package org.elasticsearch.telemetry; +import org.elasticsearch.telemetry.metric.Meter; import org.elasticsearch.telemetry.tracing.Tracer; public interface TelemetryProvider { + Tracer getTracer(); + Meter getMeter(); + TelemetryProvider NOOP = new TelemetryProvider() { @Override @@ -20,5 +24,9 @@ public Tracer getTracer() { return Tracer.NOOP; } + @Override + public Meter getMeter() { + return Meter.NOOP; + } }; } diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleCounter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleCounter.java new file mode 100644 index 0000000000000..c98701bb0a1bb --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleCounter.java @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * A monotonically increasing metric that uses a double. + * Useful for capturing the number of bytes received, number of requests, etc. + */ +public interface DoubleCounter extends Instrument { + /** + * Add one to the current counter. + */ + void increment(); + + /** + * Increment the counter. + * @param inc amount to increment, non-negative + */ + void incrementBy(double inc); + + /** + * Increment the counter. + * @param inc amount to increment, non-negative + * @param attributes key-value pairs to associate with this increment + */ + void incrementBy(double inc, Map attributes); + + /** + * Noop counter for use in tests. + */ + DoubleCounter NOOP = new DoubleCounter() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void increment() { + + } + + @Override + public void incrementBy(double inc) { + + } + + @Override + public void incrementBy(double inc, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java new file mode 100644 index 0000000000000..797c125900bb8 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * Record non-additive double values. eg number of running threads, current load + */ +public interface DoubleGauge extends Instrument { + /** + * Record the current value for measured item + */ + void record(double value); + + /** + * Record the current value + * @param attributes key-value pairs to associate with the current measurement + */ + void record(double value, Map attributes); + + /** + * Noop gauge for tests + */ + DoubleGauge NOOP = new DoubleGauge() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void record(double value) { + + } + + @Override + public void record(double value, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleHistogram.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleHistogram.java new file mode 100644 index 0000000000000..11958ea36cd3d --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleHistogram.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * Record arbitrary values that are summarized statistically, useful for percentiles and histograms. + */ +public interface DoubleHistogram extends Instrument { + /** + * Record a sample for the measured item + * @param value + */ + void record(double value); + + /** + * Record a sample for the measured item + * @param attributes key-value pairs to associate with the current sample + */ + void record(double value, Map attributes); + + /** + * Noop histogram for tests + */ + DoubleHistogram NOOP = new DoubleHistogram() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void record(double value) { + + } + + @Override + public void record(double value, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleUpDownCounter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleUpDownCounter.java new file mode 100644 index 0000000000000..7d484ebf07d32 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleUpDownCounter.java @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * A counter that supports decreasing and increasing values. + * Useful for capturing the number of requests in a queue. + */ +public interface DoubleUpDownCounter extends Instrument { + /** + * Add to the counter + * @param inc may be negative. + */ + void add(double inc); + + /** + * Add to the counter + * @param inc may be negative. + * @param attributes key-value pairs to associate with this increment + */ + void add(double inc, Map attributes); + + /** + * Noop counter for use in tests + */ + DoubleUpDownCounter NOOP = new DoubleUpDownCounter() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void add(double inc) { + + } + + @Override + public void add(double inc, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/Instrument.java b/server/src/main/java/org/elasticsearch/telemetry/metric/Instrument.java new file mode 100644 index 0000000000000..19a7e259120f2 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/Instrument.java @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +public interface Instrument { + String getName(); +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongCounter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongCounter.java new file mode 100644 index 0000000000000..f8f2150163835 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongCounter.java @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * A monotonically increasing metric that uses a long. Useful for integral values such as the number of bytes received, + * number of requests, etc. + */ +public interface LongCounter extends Instrument { + /** + * Add one to the current counter + */ + void increment(); + + /** + * Increment the counter + * @param inc amount to increment + */ + void incrementBy(long inc); + + /** + * Increment the counter. + * @param inc amount to increment + * @param attributes key-value pairs to associate with this increment + */ + void incrementBy(long inc, Map attributes); + + /** + * Noop counter for use in tests. + */ + LongCounter NOOP = new LongCounter() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void increment() { + + } + + @Override + public void incrementBy(long inc) { + + } + + @Override + public void incrementBy(long inc, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java new file mode 100644 index 0000000000000..71539064ce53e --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * Record non-additive long values. + */ +public interface LongGauge extends Instrument { + + /** + * Record the current value of the measured item. + * @param value + */ + void record(long value); + + /** + * Record the current value + * @param attributes key-value pairs to associate with the current measurement + */ + void record(long value, Map attributes); + + /** + * Noop gauge for tests + */ + LongGauge NOOP = new LongGauge() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void record(long value) { + + } + + @Override + public void record(long value, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongHistogram.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongHistogram.java new file mode 100644 index 0000000000000..27d5261f755ef --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongHistogram.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * Record arbitrary values that are summarized statistically, useful for percentiles and histograms. + */ +public interface LongHistogram extends Instrument { + /** + * Record a sample for the measured item + * @param value + */ + void record(long value); + + /** + * Record a sample for the measured item + * @param attributes key-value pairs to associate with the current sample + */ + void record(long value, Map attributes); + + /** + * Noop histogram for tests + */ + LongHistogram NOOP = new LongHistogram() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void record(long value) { + + } + + @Override + public void record(long value, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongUpDownCounter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongUpDownCounter.java new file mode 100644 index 0000000000000..f62030da8f6bd --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongUpDownCounter.java @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +/** + * A counter that supports decreasing and increasing values. + * Useful for capturing the number of requests in a queue. + */ +public interface LongUpDownCounter extends Instrument { + /** + * Add to the counter + * @param inc may be negative. + */ + void add(long inc); + + /** + * Add to the counter + * @param inc may be negative. + * @param attributes key-value pairs to associate with this increment + */ + void add(long inc, Map attributes); + + /** + * Noop counter for use in tests + */ + LongUpDownCounter NOOP = new LongUpDownCounter() { + @Override + public String getName() { + return "noop"; + } + + @Override + public void add(long inc) { + + } + + @Override + public void add(long inc, Map attributes) { + + } + }; +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java new file mode 100644 index 0000000000000..77bbf6f673fd3 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +/** + * Container for metering instruments. Meters with the same name and type (DoubleCounter, etc) can + * only be registered once. + * TODO(stu): describe name, unit and description + */ +public interface Meter { + /** + * Register a {@link DoubleCounter}. The returned object may be reused. + * @param name name of the counter + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + DoubleCounter registerDoubleCounter(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link DoubleCounter}. + * @param name name of the counter + * @return the registered meter. + */ + DoubleCounter getDoubleCounter(String name); + + /** + * Register a {@link DoubleUpDownCounter}. The returned object may be reused. + * @param name name of the counter + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link DoubleUpDownCounter}. + * @param name name of the counter + * @return the registered meter. + */ + DoubleUpDownCounter getDoubleUpDownCounter(String name); + + /** + * Register a {@link DoubleGauge}. The returned object may be reused. + * @param name name of the gauge + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + DoubleGauge registerDoubleGauge(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link DoubleGauge}. + * @param name name of the gauge + * @return the registered meter. + */ + DoubleGauge getDoubleGauge(String name); + + /** + * Register a {@link DoubleHistogram}. The returned object may be reused. + * @param name name of the histogram + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + DoubleHistogram registerDoubleHistogram(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link DoubleHistogram}. + * @param name name of the histogram + * @return the registered meter. + */ + DoubleHistogram getDoubleHistogram(String name); + + /** + * Register a {@link LongCounter}. The returned object may be reused. + * @param name name of the counter + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + LongCounter registerLongCounter(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link LongCounter}. + * @param name name of the counter + * @return the registered meter. + */ + LongCounter getLongCounter(String name); + + /** + * Register a {@link LongUpDownCounter}. The returned object may be reused. + * @param name name of the counter + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link LongUpDownCounter}. + * @param name name of the counter + * @return the registered meter. + */ + LongUpDownCounter getLongUpDownCounter(String name); + + /** + * Register a {@link LongGauge}. The returned object may be reused. + * @param name name of the gauge + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + LongGauge registerLongGauge(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link LongGauge}. + * @param name name of the gauge + * @return the registered meter. + */ + LongGauge getLongGauge(String name); + + /** + * Register a {@link LongHistogram}. The returned object may be reused. + * @param name name of the histogram + * @param description description of purpose + * @param unit the unit (bytes, sec, hour) + * @return the registered meter. + */ + LongHistogram registerLongHistogram(String name, String description, String unit); + + /** + * Retrieved a previously registered {@link LongHistogram}. + * @param name name of the histogram + * @return the registered meter. + */ + LongHistogram getLongHistogram(String name); + + /** + * Noop implementation for tests + */ + Meter NOOP = new Meter() { + @Override + public DoubleCounter registerDoubleCounter(String name, String description, String unit) { + return DoubleCounter.NOOP; + } + + @Override + public DoubleCounter getDoubleCounter(String name) { + return DoubleCounter.NOOP; + } + + public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { + return DoubleUpDownCounter.NOOP; + } + + @Override + public DoubleUpDownCounter getDoubleUpDownCounter(String name) { + return DoubleUpDownCounter.NOOP; + } + + @Override + public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + return DoubleGauge.NOOP; + } + + @Override + public DoubleGauge getDoubleGauge(String name) { + return DoubleGauge.NOOP; + } + + @Override + public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { + return DoubleHistogram.NOOP; + } + + @Override + public DoubleHistogram getDoubleHistogram(String name) { + return DoubleHistogram.NOOP; + } + + @Override + public LongCounter registerLongCounter(String name, String description, String unit) { + return LongCounter.NOOP; + } + + @Override + public LongCounter getLongCounter(String name) { + return LongCounter.NOOP; + } + + @Override + public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { + return LongUpDownCounter.NOOP; + } + + @Override + public LongUpDownCounter getLongUpDownCounter(String name) { + return LongUpDownCounter.NOOP; + } + + @Override + public LongGauge registerLongGauge(String name, String description, String unit) { + return LongGauge.NOOP; + } + + @Override + public LongGauge getLongGauge(String name) { + return LongGauge.NOOP; + } + + @Override + public LongHistogram registerLongHistogram(String name, String description, String unit) { + return LongHistogram.NOOP; + } + + @Override + public LongHistogram getLongHistogram(String name) { + return LongHistogram.NOOP; + } + }; +} From 44a2d68f197094dc3af8d053ff166e0228256a71 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Thu, 28 Sep 2023 17:45:17 -0700 Subject: [PATCH 142/155] Replace recursive with loop in PackedValuesBlockHash (#99992) This change replaces the recursion with a loop when packing and hashing multiple keys. While the recursive version is clever, it may not be as straightforward for future readers. Using a loop also helps us avoid StackOverflow when grouping by a large number of keys. --- .../blockhash/PackedValuesBlockHash.java | 192 +++++++++--------- 1 file changed, 95 insertions(+), 97 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java index 31f65e9b70053..7ecaddf2092fa 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java @@ -22,8 +22,6 @@ import org.elasticsearch.compute.operator.BatchEncoder; import org.elasticsearch.compute.operator.HashAggregationOperator; import org.elasticsearch.compute.operator.MultivalueDedupe; -import org.elasticsearch.logging.LogManager; -import org.elasticsearch.logging.Logger; import java.util.Arrays; import java.util.List; @@ -51,19 +49,20 @@ * } */ final class PackedValuesBlockHash extends BlockHash { - private static final Logger logger = LogManager.getLogger(PackedValuesBlockHash.class); static final int DEFAULT_BATCH_SIZE = Math.toIntExact(ByteSizeValue.ofKb(10).getBytes()); - private final List groups; private final int emitBatchSize; private final BytesRefHash bytesRefHash; private final int nullTrackingBytes; + private final BytesRef scratch = new BytesRef(); + private final BytesRefBuilder bytes = new BytesRefBuilder(); + private final Group[] groups; - PackedValuesBlockHash(List groups, BigArrays bigArrays, int emitBatchSize) { - this.groups = groups; + PackedValuesBlockHash(List specs, BigArrays bigArrays, int emitBatchSize) { + this.groups = specs.stream().map(Group::new).toArray(Group[]::new); this.emitBatchSize = emitBatchSize; this.bytesRefHash = new BytesRefHash(1, bigArrays); - this.nullTrackingBytes = groups.size() / 8 + 1; + this.nullTrackingBytes = (groups.length + 7) / 8; } @Override @@ -75,23 +74,28 @@ void add(Page page, GroupingAggregatorFunction.AddInput addInput, int batchSize) new AddWork(page, addInput, batchSize).add(); } + private static class Group { + final HashAggregationOperator.GroupSpec spec; + BatchEncoder encoder; + int positionOffset; + int valueOffset; + int loopedIndex; + int valueCount; + int bytesStart; + + Group(HashAggregationOperator.GroupSpec spec) { + this.spec = spec; + } + } + class AddWork extends LongLongBlockHash.AbstractAddBlock { - final BatchEncoder[] encoders = new BatchEncoder[groups.size()]; - final int[] positionOffsets = new int[groups.size()]; - final int[] valueOffsets = new int[groups.size()]; - final BytesRef[] scratches = new BytesRef[groups.size()]; - final BytesRefBuilder bytes = new BytesRefBuilder(); final int positionCount; - int position; - int count; - int bufferedGroup; AddWork(Page page, GroupingAggregatorFunction.AddInput addInput, int batchSize) { super(emitBatchSize, addInput); - for (int g = 0; g < groups.size(); g++) { - encoders[g] = MultivalueDedupe.batchEncoder(page.getBlock(groups.get(g).channel()), batchSize); - scratches[g] = new BytesRef(); + for (Group group : groups) { + group.encoder = MultivalueDedupe.batchEncoder(page.getBlock(group.spec.channel()), batchSize); } bytes.grow(nullTrackingBytes); this.positionCount = page.getPositionCount(); @@ -104,91 +108,86 @@ class AddWork extends LongLongBlockHash.AbstractAddBlock { */ void add() { for (position = 0; position < positionCount; position++) { - if (logger.isTraceEnabled()) { - logger.trace("position {}", position); - } // Make sure all encoders have encoded the current position and the offsets are queued to it's start - for (int g = 0; g < encoders.length; g++) { - positionOffsets[g]++; - while (positionOffsets[g] >= encoders[g].positionCount()) { - encoders[g].encodeNextBatch(); - positionOffsets[g] = 0; - valueOffsets[g] = 0; + boolean singleEntry = true; + for (Group g : groups) { + var encoder = g.encoder; + g.positionOffset++; + while (g.positionOffset >= encoder.positionCount()) { + encoder.encodeNextBatch(); + g.positionOffset = 0; + g.valueOffset = 0; } + g.valueCount = encoder.valueCount(g.positionOffset); + singleEntry &= (g.valueCount == 1); } - - count = 0; Arrays.fill(bytes.bytes(), 0, nullTrackingBytes, (byte) 0); bytes.setLength(nullTrackingBytes); - addPosition(0); - switch (count) { - case 0 -> throw new IllegalStateException("didn't find any values"); - case 1 -> { - ords.appendInt(bufferedGroup); - addedValue(position); - } - default -> ords.endPositionEntry(); - } - for (int g = 0; g < encoders.length; g++) { - valueOffsets[g] += encoders[g].valueCount(positionOffsets[g]); + if (singleEntry) { + addSingleEntry(); + } else { + addMultipleEntries(); } } emitOrds(); } - private void addPosition(int g) { - if (g == groups.size()) { - addBytes(); - return; - } - int start = bytes.length(); - int count = encoders[g].valueCount(positionOffsets[g]); - assert count > 0; - int valueOffset = valueOffsets[g]; - BytesRef v = encoders[g].read(valueOffset++, scratches[g]); - if (logger.isTraceEnabled()) { - logger.trace("\t".repeat(g + 1) + v); - } - if (v.length == 0) { - assert count == 1 : "null value in non-singleton list"; - int nullByte = g / 8; - int nullShift = g % 8; - bytes.bytes()[nullByte] |= (byte) (1 << nullShift); - } - bytes.setLength(start); - bytes.append(v); - addPosition(g + 1); // TODO stack overflow protection - for (int i = 1; i < count; i++) { - v = encoders[g].read(valueOffset++, scratches[g]); - if (logger.isTraceEnabled()) { - logger.trace("\t".repeat(g + 1) + v); + private void addSingleEntry() { + for (int g = 0; g < groups.length; g++) { + Group group = groups[g]; + BytesRef v = group.encoder.read(group.valueOffset++, scratch); + if (v.length == 0) { + int nullByte = g / 8; + int nullShift = g % 8; + bytes.bytes()[nullByte] |= (byte) (1 << nullShift); + } else { + bytes.append(v); } - assert v.length > 0 : "null value after the first position"; - bytes.setLength(start); - bytes.append(v); - addPosition(g + 1); } + int ord = Math.toIntExact(hashOrdToGroup(bytesRefHash.add(bytes.get()))); + ords.appendInt(ord); + addedValue(position); } - private void addBytes() { - int group = Math.toIntExact(hashOrdToGroup(bytesRefHash.add(bytes.get()))); - switch (count) { - case 0 -> bufferedGroup = group; - case 1 -> { - ords.beginPositionEntry(); - ords.appendInt(bufferedGroup); - addedValueInMultivaluePosition(position); - ords.appendInt(group); - addedValueInMultivaluePosition(position); + private void addMultipleEntries() { + ords.beginPositionEntry(); + int g = 0; + outer: for (;;) { + for (; g < groups.length; g++) { + Group group = groups[g]; + group.bytesStart = bytes.length(); + BytesRef v = group.encoder.read(group.valueOffset + group.loopedIndex, scratch); + ++group.loopedIndex; + if (v.length == 0) { + assert group.valueCount == 1 : "null value in non-singleton list"; + int nullByte = g / 8; + int nullShift = g % 8; + bytes.bytes()[nullByte] |= (byte) (1 << nullShift); + } else { + bytes.append(v); + } } - default -> { - ords.appendInt(group); - addedValueInMultivaluePosition(position); + // emit ords + int ord = Math.toIntExact(hashOrdToGroup(bytesRefHash.add(bytes.get()))); + ords.appendInt(ord); + addedValueInMultivaluePosition(position); + + // rewind + Group group = groups[--g]; + bytes.setLength(group.bytesStart); + while (group.loopedIndex == group.valueCount) { + group.loopedIndex = 0; + if (g == 0) { + break outer; + } else { + group = groups[--g]; + bytes.setLength(group.bytesStart); + } } } - count++; - if (logger.isTraceEnabled()) { - logger.trace("{} = {}", bytes.get(), group); + ords.endPositionEntry(); + for (Group group : groups) { + group.valueOffset += group.valueCount; } } } @@ -196,16 +195,16 @@ private void addBytes() { @Override public Block[] getKeys() { int size = Math.toIntExact(bytesRefHash.size()); - BatchEncoder.Decoder[] decoders = new BatchEncoder.Decoder[groups.size()]; - Block.Builder[] builders = new Block.Builder[groups.size()]; + BatchEncoder.Decoder[] decoders = new BatchEncoder.Decoder[groups.length]; + Block.Builder[] builders = new Block.Builder[groups.length]; for (int g = 0; g < builders.length; g++) { - ElementType elementType = groups.get(g).elementType(); + ElementType elementType = groups[g].spec.elementType(); decoders[g] = BatchEncoder.decoder(elementType); builders[g] = elementType.newBlockBuilder(size); } - BytesRef values[] = new BytesRef[(int) Math.min(100, bytesRefHash.size())]; - BytesRef nulls[] = new BytesRef[values.length]; + BytesRef[] values = new BytesRef[(int) Math.min(100, bytesRefHash.size())]; + BytesRef[] nulls = new BytesRef[values.length]; for (int offset = 0; offset < values.length; offset++) { values[offset] = new BytesRef(); nulls[offset] = new BytesRef(); @@ -231,7 +230,7 @@ public Block[] getKeys() { readKeys(decoders, builders, nulls, values, offset); } - Block[] keyBlocks = new Block[groups.size()]; + Block[] keyBlocks = new Block[groups.length]; for (int g = 0; g < keyBlocks.length; g++) { keyBlocks[g] = builders[g].build(); } @@ -271,13 +270,12 @@ public String toString() { StringBuilder b = new StringBuilder(); b.append("PackedValuesBlockHash{groups=["); boolean first = true; - for (HashAggregationOperator.GroupSpec spec : groups) { - if (first) { - first = false; - } else { + for (int i = 0; i < groups.length; i++) { + if (i > 0) { b.append(", "); } - b.append(spec.channel()).append(':').append(spec.elementType()); + Group group = groups[i]; + b.append(group.spec.channel()).append(':').append(group.spec.elementType()); } b.append("], entries=").append(bytesRefHash.size()); b.append(", size=").append(ByteSizeValue.ofBytes(bytesRefHash.ramBytesUsed())); From 05189ef395e4d28c5fbc5612c7f27954648680bd Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 29 Sep 2023 08:22:20 +0300 Subject: [PATCH 143/155] Debug log OIDC ID token validation exception (#100002) This ensures we debug log OIDC ID token validation exceptions, which should help admins with troubleshooting the OIDC realm setup. --- .../authc/jwt/JwtSignatureValidator.java | 25 +++++++------------ .../xpack/security/authc/jwt/JwtUtil.java | 23 +++++++++++++++++ .../oidc/OpenIdConnectAuthenticator.java | 9 +++++-- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtSignatureValidator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtSignatureValidator.java index 91224a8246169..b1ee1b77998ec 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtSignatureValidator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtSignatureValidator.java @@ -17,7 +17,6 @@ import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.OctetSequenceKey; import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jwt.SignedJWT; import org.apache.logging.log4j.LogManager; @@ -40,9 +39,10 @@ import java.util.Arrays; import java.util.List; -import java.util.function.Supplier; import java.util.stream.Stream; +import static org.elasticsearch.xpack.security.authc.jwt.JwtUtil.toStringRedactSignature; + public interface JwtSignatureValidator extends Releasable { Logger logger = LogManager.getLogger(JwtSignatureValidator.class); @@ -361,7 +361,7 @@ default void validateSignature(final SignedJWT jwt, final List jwks) throws final String id = jwt.getHeader().getKeyID(); final JWSAlgorithm alg = jwt.getHeader().getAlgorithm(); - tracer.append("Filtering [{}] possible JWKs to verifying signature for JWT [{}].", jwks.size(), getSafePrintableJWT(jwt)); + tracer.append("Filtering [{}] possible JWKs to verifying signature for JWT [{}].", jwks.size(), toStringRedactSignature(jwt)); // If JWT has optional kid header, and realm JWKs have optional kid attribute, any mismatches JWT.kid vs JWK.kid can be ignored. // Keep any JWKs if JWK optional kid attribute is missing. Keep all JWKs if JWT optional kid header is missing. @@ -399,7 +399,11 @@ default void validateSignature(final SignedJWT jwt, final List jwks) throws int attempt = 0; int maxAttempts = jwksConfigured.size(); - tracer.append("Attempting to verify signature for JWT [{}] against [{}] possible JWKs.", getSafePrintableJWT(jwt), maxAttempts); + tracer.append( + "Attempting to verify signature for JWT [{}] against [{}] possible JWKs.", + toStringRedactSignature(jwt), + maxAttempts + ); for (final JWK jwk : jwksConfigured) { attempt++; if (jwt.verify(createJwsVerifier(jwk))) { @@ -429,7 +433,7 @@ default void validateSignature(final SignedJWT jwt, final List jwks) throws ); } } - throw new ElasticsearchException("JWT [" + getSafePrintableJWT(jwt).get() + "] signature verification failed."); + throw new ElasticsearchException("JWT [" + toStringRedactSignature(jwt).get() + "] signature verification failed."); } } @@ -458,15 +462,4 @@ interface PkcJwkSetReloadNotifier { void reloaded(); } - /** - * @param jwt The signed JWT - * @return A print safe supplier to describe a JWT that redacts the signature. While the signature is not generally sensitive, - * we don't want to leak the entire JWT to the log to avoid a possible replay. - */ - private Supplier getSafePrintableJWT(SignedJWT jwt) { - Base64URL[] parts = jwt.getParsedParts(); - assert parts.length == 3; - return () -> parts[0].toString() + "." + parts[1].toString() + "."; - } - } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtUtil.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtUtil.java index 3e3533f028b38..9168c5c0925bd 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtUtil.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtUtil.java @@ -7,9 +7,12 @@ package org.elasticsearch.xpack.security.authc.jwt; +import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jose.util.JSONObjectUtils; +import com.nimbusds.jwt.JWT; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -59,6 +62,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; @@ -389,4 +393,23 @@ public void close() { closed = true; } } + + /** + * @param jwt The signed JWT + * @return A print safe supplier to describe a JWT that redacts the signature. While the signature is not generally sensitive, + * we don't want to leak the entire JWT to the log to avoid a possible replay. + */ + public static Supplier toStringRedactSignature(JWT jwt) { + if (jwt instanceof JWSObject) { + Base64URL[] parts = jwt.getParsedParts(); + assert parts.length == 3; + assert parts[0] != null; + assert parts[1] != null; + assert parts[2] != null; + assert Objects.equals(parts[2], ((JWSObject) jwt).getSignature()); + return () -> parts[0] + "." + parts[1] + "."; + } else { + return jwt::getParsedString; + } + } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java index 73bc36c94e2d5..754d2a82dd835 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java @@ -93,6 +93,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings; import org.elasticsearch.xpack.core.ssl.SSLService; +import org.elasticsearch.xpack.security.authc.jwt.JwtUtil; import java.io.IOException; import java.net.URI; @@ -293,14 +294,18 @@ void getUserClaims( .triggerReload(ActionListener.wrap(v -> { getUserClaims(accessToken, idToken, expectedNonce, false, claimsListener); }, ex -> { - LOGGER.trace("Attempted and failed to refresh JWK cache upon token validation failure", e); + LOGGER.debug("Attempted and failed to refresh JWK cache upon token validation failure", e); claimsListener.onFailure(ex); })); } else { + LOGGER.debug("Failed to parse or validate the ID Token", e); claimsListener.onFailure(new ElasticsearchSecurityException("Failed to parse or validate the ID Token", e)); } } catch (com.nimbusds.oauth2.sdk.ParseException | ParseException | JOSEException e) { - LOGGER.debug("ID Token: [{}], Nonce: [{}]", idToken.getParsedString(), expectedNonce); + LOGGER.debug( + () -> format("ID Token: [%s], Nonce: [%s]", JwtUtil.toStringRedactSignature(idToken).get(), expectedNonce.toString()), + e + ); claimsListener.onFailure(new ElasticsearchSecurityException("Failed to parse or validate the ID Token", e)); } } From fc962ff32ba0f48ae539def65e0f44a12c0dfec4 Mon Sep 17 00:00:00 2001 From: Matteo Piergiovanni <134913285+piergm@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:23:57 +0200 Subject: [PATCH 144/155] [CI] SearchResponseTests#testSerialization failing resolved (#100020) Removed class variables running, partial and failed that are now calculated when needed. Having them caused issues due to the fact that they always reflected the content of the clusterInfo map that could be updated in various part of the code and the update would not reflect on those counters. Updated class methods equals and hashCode accordingly. Unmuted test related to issue --- docs/changelog/100020.yaml | 6 ++ .../action/search/SearchResponse.java | 63 +++++++++---------- .../action/search/SearchResponseTests.java | 1 - 3 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 docs/changelog/100020.yaml diff --git a/docs/changelog/100020.yaml b/docs/changelog/100020.yaml new file mode 100644 index 0000000000000..9f97778860eef --- /dev/null +++ b/docs/changelog/100020.yaml @@ -0,0 +1,6 @@ +pr: 100020 +summary: "[CI] `SearchResponseTests#testSerialization` failing resolved" +area: Search +type: bug +issues: + - 100005 diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index f9f5f43494711..ba785fd4d9637 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -471,9 +471,6 @@ public static class Clusters implements ToXContentFragment, Writeable { private final int total; private final int successful; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map private final int skipped; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map - private final int running; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map - private final int partial; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map - private final int failed; // not used for minimize_roundtrips=true; dynamically determined from clusterInfo map // key to map is clusterAlias on the primary querying cluster of a CCS minimize_roundtrips=true query // the Map itself is immutable after construction - all Clusters will be accounted for at the start of the search @@ -503,6 +500,8 @@ public Clusters( assert remoteClusterIndices.size() > 0 : "At least one remote cluster must be passed into this Cluster constructor"; this.total = remoteClusterIndices.size() + (localIndices == null ? 0 : 1); assert total >= 1 : "No local indices or remote clusters passed in"; + this.successful = 0; // calculated from clusterInfo map for minimize_roundtrips + this.skipped = 0; // calculated from clusterInfo map for minimize_roundtrips this.ccsMinimizeRoundtrips = ccsMinimizeRoundtrips; Map> m = new HashMap<>(); if (localIndices != null) { @@ -517,11 +516,6 @@ public Clusters( m.put(clusterAlias, new AtomicReference<>(c)); } this.clusterInfo = Collections.unmodifiableMap(m); - this.successful = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.SUCCESSFUL); - this.skipped = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.SKIPPED); - this.running = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.RUNNING); - this.partial = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.PARTIAL); - this.failed = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.FAILED); } /** @@ -539,36 +533,39 @@ public Clusters(int total, int successful, int skipped) { this.total = total; this.successful = successful; this.skipped = skipped; - this.running = 0; - this.partial = 0; - this.failed = 0; this.ccsMinimizeRoundtrips = false; this.clusterInfo = Collections.emptyMap(); // will never be used if created from this constructor } public Clusters(StreamInput in) throws IOException { this.total = in.readVInt(); - this.successful = in.readVInt(); - this.skipped = in.readVInt(); + int successfulTemp = in.readVInt(); + int skippedTemp = in.readVInt(); if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_500_053)) { List clusterList = in.readCollectionAsList(Cluster::new); if (clusterList.isEmpty()) { this.clusterInfo = Collections.emptyMap(); + this.successful = successfulTemp; + this.skipped = skippedTemp; } else { Map> m = new HashMap<>(); clusterList.forEach(c -> m.put(c.getClusterAlias(), new AtomicReference<>(c))); this.clusterInfo = Collections.unmodifiableMap(m); + this.successful = getClusterStateCount(Cluster.Status.SUCCESSFUL); + this.skipped = getClusterStateCount(Cluster.Status.SKIPPED); } } else { + this.successful = successfulTemp; + this.skipped = skippedTemp; this.clusterInfo = Collections.emptyMap(); } - this.running = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.RUNNING); - this.partial = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.PARTIAL); - this.failed = determineCountFromClusterInfo(cluster -> cluster.getStatus() == Cluster.Status.FAILED); + int running = getClusterStateCount(Cluster.Status.RUNNING); + int partial = getClusterStateCount(Cluster.Status.PARTIAL); + int failed = getClusterStateCount(Cluster.Status.FAILED); this.ccsMinimizeRoundtrips = false; assert total >= 0 : "total is negative: " + total; - assert total >= successful + skipped + running + partial + failed - : "successful + skipped + running + partial + failed is larger than total. total: " + assert total == successful + skipped + running + partial + failed + : "successful + skipped + running + partial + failed is not equal to total. total: " + total + " successful: " + successful @@ -586,11 +583,8 @@ private Clusters(Map> clusterInfoMap) { assert clusterInfoMap.size() > 0 : "this constructor should not be called with an empty Cluster info map"; this.total = clusterInfoMap.size(); this.clusterInfo = clusterInfoMap; - this.successful = 0; // calculated from clusterInfo map for minimize_roundtrips - this.skipped = 0; // calculated from clusterInfo map for minimize_roundtrips - this.running = 0; // calculated from clusterInfo map for minimize_roundtrips - this.partial = 0; // calculated from clusterInfo map for minimize_roundtrips - this.failed = 0; // calculated from clusterInfo map for minimize_roundtrips + this.successful = getClusterStateCount(Cluster.Status.SUCCESSFUL); + this.skipped = getClusterStateCount(Cluster.Status.SKIPPED); // should only be called if "details" section of fromXContent is present (for ccsMinimizeRoundtrips) this.ccsMinimizeRoundtrips = true; } @@ -705,11 +699,9 @@ public int getTotal() { public int getClusterStateCount(Cluster.Status status) { if (clusterInfo.isEmpty()) { return switch (status) { - case RUNNING -> running; case SUCCESSFUL -> successful; - case PARTIAL -> partial; case SKIPPED -> skipped; - case FAILED -> failed; + default -> 0; }; } else { return determineCountFromClusterInfo(cluster -> cluster.getStatus() == status); @@ -752,16 +744,23 @@ public boolean equals(Object o) { } Clusters clusters = (Clusters) o; return total == clusters.total - && successful == clusters.successful - && skipped == clusters.skipped - && running == clusters.running - && partial == clusters.partial - && failed == clusters.failed; + && getClusterStateCount(Cluster.Status.SUCCESSFUL) == clusters.getClusterStateCount(Cluster.Status.SUCCESSFUL) + && getClusterStateCount(Cluster.Status.SKIPPED) == clusters.getClusterStateCount(Cluster.Status.SKIPPED) + && getClusterStateCount(Cluster.Status.RUNNING) == clusters.getClusterStateCount(Cluster.Status.RUNNING) + && getClusterStateCount(Cluster.Status.PARTIAL) == clusters.getClusterStateCount(Cluster.Status.PARTIAL) + && getClusterStateCount(Cluster.Status.FAILED) == clusters.getClusterStateCount(Cluster.Status.FAILED); } @Override public int hashCode() { - return Objects.hash(total, successful, skipped, running, partial, failed); + return Objects.hash( + total, + getClusterStateCount(Cluster.Status.SUCCESSFUL), + getClusterStateCount(Cluster.Status.SKIPPED), + getClusterStateCount(Cluster.Status.RUNNING), + getClusterStateCount(Cluster.Status.PARTIAL), + getClusterStateCount(Cluster.Status.FAILED) + ); } @Override diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index 9acb63db2cc90..befea803e6fa0 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -590,7 +590,6 @@ public void testToXContent() throws IOException { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100005") public void testSerialization() throws IOException { SearchResponse searchResponse = createTestItem(false); SearchResponse deserialized = copyWriteable( From 9d01def3dce9349d73f19f23f5b1ad4c41cf1646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Fri, 29 Sep 2023 09:24:36 +0200 Subject: [PATCH 145/155] [DOCS] Changes semantic search tutorials to use ELSER v2 and sparse_vector field type (#100021) * [DOCS] Changes semantic search tutorials to use ELSER v2 and sparse_vector field type. * [DOCS] More edits. --- .../semantic-search-elser.asciidoc | 35 ++++++++++--------- .../semantic-search/field-mappings.asciidoc | 10 +++--- .../generate-embeddings.asciidoc | 2 +- .../semantic-search/hybrid-search.asciidoc | 2 +- .../semantic-search/search.asciidoc | 4 +-- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/docs/reference/search/search-your-data/semantic-search-elser.asciidoc b/docs/reference/search/search-your-data/semantic-search-elser.asciidoc index 0f07f1f4128fe..082bb2ae2e020 100644 --- a/docs/reference/search/search-your-data/semantic-search-elser.asciidoc +++ b/docs/reference/search/search-your-data/semantic-search-elser.asciidoc @@ -14,7 +14,7 @@ The instructions in this tutorial shows you how to use ELSER to perform semantic search on your data. NOTE: Only the first 512 extracted tokens per field are considered during -semantic search with ELSER v1. Refer to +semantic search with ELSER. Refer to {ml-docs}/ml-nlp-limitations.html#ml-nlp-elser-v1-limit-512[this page] for more information. @@ -44,15 +44,16 @@ you must provide suitably sized nodes yourself. First, the mapping of the destination index - the index that contains the tokens that the model created based on your text - must be created. The destination -index must have a field with the <> field type -to index the ELSER output. +index must have a field with the +<> field type to index the +ELSER output. -NOTE: ELSER output must be ingested into a field with the `rank_features` field -type. Otherwise, {es} interprets the token-weight pairs as a massive amount of -fields in a document. If you get an error similar to this +NOTE: ELSER output must be ingested into a field with the `sparse_vector` or +`rank_features` field type. Otherwise, {es} interprets the token-weight pairs as +a massive amount of fields in a document. If you get an error similar to this `"Limit of total fields [1000] has been exceeded while adding new fields"` then the ELSER output field is not mapped properly and it has a field type different -than `rank_features`. +than `sparse_vector` or `rank_features`. [source,console] ---- @@ -61,7 +62,7 @@ PUT my-index "mappings": { "properties": { "ml.tokens": { <1> - "type": "rank_features" <2> + "type": "sparse_vector" <2> }, "text": { <3> "type": "text" <4> @@ -72,7 +73,7 @@ PUT my-index ---- // TEST[skip:TBD] <1> The name of the field to contain the generated tokens. -<2> The field to contain the tokens is a `rank_features` field. +<2> The field to contain the tokens is a `sparse_vector` field. <3> The name of the field from which to create the sparse vector representation. In this example, the name of the field is `text`. <4> The field type which is text in this example. @@ -90,12 +91,12 @@ that is being ingested in the pipeline. [source,console] ---- -PUT _ingest/pipeline/elser-v1-test +PUT _ingest/pipeline/elser-v2-test { "processors": [ { "inference": { - "model_id": ".elser_model_1", + "model_id": ".elser_model_2", "target_field": "ml", "field_map": { <1> "text": "text_field" @@ -155,7 +156,7 @@ POST _reindex?wait_for_completion=false }, "dest": { "index": "my-index", - "pipeline": "elser-v1-test" + "pipeline": "elser-v2-test" } } ---- @@ -192,7 +193,7 @@ GET my-index/_search "query":{ "text_expansion":{ "ml.tokens":{ - "model_id":".elser_model_1", + "model_id":".elser_model_2", "model_text":"How to avoid muscle soreness after running?" } } @@ -236,7 +237,7 @@ weights. "exercises":0.36694175, (...) }, - "model_id":".elser_model_1" + "model_id":".elser_model_2" } } }, @@ -276,7 +277,7 @@ GET my-index/_search "text_expansion": { "ml.tokens": { "model_text": "How to avoid muscle soreness after running?", - "model_id": ".elser_model_1", + "model_id": ".elser_model_2", "boost": 1 <2> } } @@ -342,7 +343,7 @@ PUT my-index }, "properties": { "ml.tokens": { - "type": "rank_features" + "type": "sparse_vector" }, "text": { "type": "text" @@ -359,7 +360,7 @@ PUT my-index ==== Further reading * {ml-docs}/ml-nlp-elser.html[How to download and deploy ELSER] -* {ml-docs}/ml-nlp-limitations.html#ml-nlp-elser-v1-limit-512[ELSER v1 limitation] +* {ml-docs}/ml-nlp-limitations.html#ml-nlp-elser-v1-limit-512[ELSER limitation] * https://www.elastic.co/blog/may-2023-launch-information-retrieval-elasticsearch-ai-model[Improving information retrieval in the Elastic Stack: Introducing Elastic Learned Sparse Encoder, our new retrieval model] [discrete] diff --git a/docs/reference/tab-widgets/semantic-search/field-mappings.asciidoc b/docs/reference/tab-widgets/semantic-search/field-mappings.asciidoc index 228b7a9202341..0228078e8ce39 100644 --- a/docs/reference/tab-widgets/semantic-search/field-mappings.asciidoc +++ b/docs/reference/tab-widgets/semantic-search/field-mappings.asciidoc @@ -1,15 +1,15 @@ // tag::elser[] ELSER produces token-weight pairs as output from the input text and the query. -The {es} <> field type can store these +The {es} <> field type can store these token-weight pairs as numeric feature vectors. The index must have a field with -the `rank_features` field type to index the tokens that ELSER generates. +the `sparse_vector` field type to index the tokens that ELSER generates. To create a mapping for your ELSER index, refer to the <> of the tutorial. The example shows how to create an index mapping for `my-index` that defines the `my_embeddings.tokens` field - which will contain the ELSER output - as a -`rank_features` field. +`sparse_vector` field. [source,console] ---- @@ -18,7 +18,7 @@ PUT my-index "mappings": { "properties": { "my_embeddings.tokens": { <1> - "type": "rank_features" <2> + "type": "sparse_vector" <2> }, "my_text_field": { <3> "type": "text" <4> @@ -28,7 +28,7 @@ PUT my-index } ---- <1> The name of the field that will contain the tokens generated by ELSER. -<2> The field that contains the tokens must be a `rank_features` field. +<2> The field that contains the tokens must be a `sparse_vector` field. <3> The name of the field from which to create the sparse vector representation. In this example, the name of the field is `my_text_field`. <4> The field type is `text` in this example. diff --git a/docs/reference/tab-widgets/semantic-search/generate-embeddings.asciidoc b/docs/reference/tab-widgets/semantic-search/generate-embeddings.asciidoc index 0adfda5c2bff9..786f40fe141bd 100644 --- a/docs/reference/tab-widgets/semantic-search/generate-embeddings.asciidoc +++ b/docs/reference/tab-widgets/semantic-search/generate-embeddings.asciidoc @@ -21,7 +21,7 @@ PUT _ingest/pipeline/my-text-embeddings-pipeline "processors": [ { "inference": { - "model_id": ".elser_model_1", + "model_id": ".elser_model_2", "target_field": "my_embeddings", "field_map": { <1> "my_text_field": "text_field" diff --git a/docs/reference/tab-widgets/semantic-search/hybrid-search.asciidoc b/docs/reference/tab-widgets/semantic-search/hybrid-search.asciidoc index 26fc25c2385c8..a99bdf3c8722b 100644 --- a/docs/reference/tab-widgets/semantic-search/hybrid-search.asciidoc +++ b/docs/reference/tab-widgets/semantic-search/hybrid-search.asciidoc @@ -22,7 +22,7 @@ GET my-index/_search "query": { "text_expansion": { "my_embeddings.tokens": { - "model_id": ".elser_model_1", + "model_id": ".elser_model_2", "model_text": "the query string" } } diff --git a/docs/reference/tab-widgets/semantic-search/search.asciidoc b/docs/reference/tab-widgets/semantic-search/search.asciidoc index 425b797789270..d1cd31fbe4309 100644 --- a/docs/reference/tab-widgets/semantic-search/search.asciidoc +++ b/docs/reference/tab-widgets/semantic-search/search.asciidoc @@ -12,7 +12,7 @@ GET my-index/_search "query":{ "text_expansion":{ "my_embeddings.tokens":{ <1> - "model_id":".elser_model_1", + "model_id":".elser_model_2", "model_text":"the query string" } } @@ -20,7 +20,7 @@ GET my-index/_search } ---- // TEST[skip:TBD] -<1> The field of type `rank_features`. +<1> The field of type `sparse_vector`. // end::elser[] From c7705aa32abc6b5e3ce34c4edde6fb8ee68101bf Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:42:01 +0300 Subject: [PATCH 146/155] Improve time-series error and documentation (#100018) * Improve time-series error and documentation * spotless fix * Update docs/changelog/100018.yaml * Fix changelist * Change exception type --- docs/changelog/100018.yaml | 5 +++++ .../aggregations/bucket/time-series-aggregation.asciidoc | 4 ---- .../search/aggregations/AggregationExecutionContext.java | 6 ++++++ 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 docs/changelog/100018.yaml diff --git a/docs/changelog/100018.yaml b/docs/changelog/100018.yaml new file mode 100644 index 0000000000000..b39089db568c0 --- /dev/null +++ b/docs/changelog/100018.yaml @@ -0,0 +1,5 @@ +pr: 100018 +summary: Improve time-series error and documentation +area: "TSDB" +type: enhancement +issues: [] diff --git a/docs/reference/aggregations/bucket/time-series-aggregation.asciidoc b/docs/reference/aggregations/bucket/time-series-aggregation.asciidoc index 54638083b1053..d93df55118a8b 100644 --- a/docs/reference/aggregations/bucket/time-series-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/time-series-aggregation.asciidoc @@ -67,8 +67,6 @@ PUT /my-time-series-index-0/_bulk -------------------------------------------------- // NOTCONSOLE -////////////////////////// - To perform a time series aggregation, specify "time_series" as the aggregation type. When the boolean "keyed" is true, each bucket is given a unique key. @@ -85,8 +83,6 @@ GET /_search -------------------------------------------------- // NOTCONSOLE -////////////////////////// - This will return all results in the time series, however a more typical query will use sub aggregations to reduce the date returned to something more relevant. diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationExecutionContext.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationExecutionContext.java index 88a78a512d1bd..273df99f6479c 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationExecutionContext.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationExecutionContext.java @@ -53,6 +53,12 @@ public long getTimestamp() { } public int getTsidOrd() { + if (tsidOrdProvider == null) { + throw new IllegalArgumentException( + "Aggregation on a time-series field is misconfigured, likely due to lack of wrapping " + + "a metric aggregation within a `time-series` aggregation" + ); + } return tsidOrdProvider.getAsInt(); } } From b30813bd37417ea0e01581c1a61b1574200caa2c Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 29 Sep 2023 09:54:38 +0200 Subject: [PATCH 147/155] ESQL: add driverContext to conversion function evaluators and use blockFactory to instantiate blocks (#100016) Let convert functions (eg. `to_string()`, `to_int()`, `to_ip()` and so on) use DriverContext.blockFactory() to generate new blocks, so that they are properly attached to the circuit breaker. --- .../gen/ConvertEvaluatorImplementer.java | 68 ++++++------------- .../convert/ToBooleanFromDoubleEvaluator.java | 32 ++++----- .../convert/ToBooleanFromIntEvaluator.java | 32 ++++----- .../convert/ToBooleanFromLongEvaluator.java | 32 ++++----- .../convert/ToBooleanFromStringEvaluator.java | 32 ++++----- .../ToBooleanFromUnsignedLongEvaluator.java | 32 ++++----- .../ToDatetimeFromStringEvaluator.java | 32 ++++----- .../scalar/convert/ToDegreesEvaluator.java | 32 ++++----- .../convert/ToDoubleFromBooleanEvaluator.java | 32 ++++----- .../convert/ToDoubleFromIntEvaluator.java | 32 ++++----- .../convert/ToDoubleFromLongEvaluator.java | 32 ++++----- .../convert/ToDoubleFromStringEvaluator.java | 32 ++++----- .../ToDoubleFromUnsignedLongEvaluator.java | 32 ++++----- .../convert/ToIPFromStringEvaluator.java | 35 ++++------ .../ToIntegerFromBooleanEvaluator.java | 32 ++++----- .../convert/ToIntegerFromDoubleEvaluator.java | 32 ++++----- .../convert/ToIntegerFromLongEvaluator.java | 32 ++++----- .../convert/ToIntegerFromStringEvaluator.java | 32 ++++----- .../ToIntegerFromUnsignedLongEvaluator.java | 32 ++++----- .../convert/ToLongFromBooleanEvaluator.java | 32 ++++----- .../convert/ToLongFromDoubleEvaluator.java | 32 ++++----- .../convert/ToLongFromIntEvaluator.java | 32 ++++----- .../convert/ToLongFromStringEvaluator.java | 32 ++++----- .../ToLongFromUnsignedLongEvaluator.java | 32 ++++----- .../scalar/convert/ToRadiansEvaluator.java | 32 ++++----- .../convert/ToStringFromBooleanEvaluator.java | 35 ++++------ .../ToStringFromDatetimeEvaluator.java | 35 ++++------ .../convert/ToStringFromDoubleEvaluator.java | 35 ++++------ .../convert/ToStringFromIPEvaluator.java | 35 ++++------ .../convert/ToStringFromIntEvaluator.java | 35 ++++------ .../convert/ToStringFromLongEvaluator.java | 35 ++++------ .../ToStringFromUnsignedLongEvaluator.java | 35 ++++------ .../convert/ToStringFromVersionEvaluator.java | 35 ++++------ .../ToUnsignedLongFromBooleanEvaluator.java | 32 ++++----- .../ToUnsignedLongFromDoubleEvaluator.java | 32 ++++----- .../ToUnsignedLongFromIntEvaluator.java | 32 ++++----- .../ToUnsignedLongFromLongEvaluator.java | 32 ++++----- .../ToUnsignedLongFromStringEvaluator.java | 32 ++++----- .../convert/ToVersionFromStringEvaluator.java | 35 ++++------ .../convert/AbstractConvertFunction.java | 7 +- .../function/scalar/convert/ToBoolean.java | 14 ++-- .../function/scalar/convert/ToDatetime.java | 16 +++-- .../function/scalar/convert/ToDegrees.java | 30 ++++++-- .../function/scalar/convert/ToDouble.java | 14 ++-- .../function/scalar/convert/ToIP.java | 17 +++-- .../function/scalar/convert/ToInteger.java | 14 ++-- .../function/scalar/convert/ToLong.java | 16 +++-- .../function/scalar/convert/ToRadians.java | 30 ++++++-- .../function/scalar/convert/ToString.java | 16 +++-- .../scalar/convert/ToUnsignedLong.java | 14 ++-- .../function/scalar/convert/ToVersion.java | 14 ++-- 51 files changed, 654 insertions(+), 862 deletions(-) diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java index af42d94c236f2..d5e38127cdec7 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java @@ -13,8 +13,6 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; -import java.util.BitSet; - import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -23,18 +21,13 @@ import static org.elasticsearch.compute.gen.Methods.appendMethod; import static org.elasticsearch.compute.gen.Methods.getMethod; import static org.elasticsearch.compute.gen.Types.ABSTRACT_CONVERT_FUNCTION_EVALUATOR; -import static org.elasticsearch.compute.gen.Types.BIG_ARRAYS; import static org.elasticsearch.compute.gen.Types.BLOCK; import static org.elasticsearch.compute.gen.Types.BYTES_REF; -import static org.elasticsearch.compute.gen.Types.BYTES_REF_ARRAY; -import static org.elasticsearch.compute.gen.Types.BYTES_REF_BLOCK; +import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; import static org.elasticsearch.compute.gen.Types.SOURCE; import static org.elasticsearch.compute.gen.Types.VECTOR; -import static org.elasticsearch.compute.gen.Types.arrayBlockType; -import static org.elasticsearch.compute.gen.Types.arrayVectorType; import static org.elasticsearch.compute.gen.Types.blockType; -import static org.elasticsearch.compute.gen.Types.constantVectorType; import static org.elasticsearch.compute.gen.Types.vectorType; public class ConvertEvaluatorImplementer { @@ -79,6 +72,8 @@ private TypeSpec type() { builder.addModifiers(Modifier.PUBLIC, Modifier.FINAL); builder.superclass(ABSTRACT_CONVERT_FUNCTION_EVALUATOR); + builder.addField(DRIVER_CONTEXT, "driverContext", Modifier.PRIVATE, Modifier.FINAL); + builder.addMethod(ctor()); builder.addMethod(name()); builder.addMethod(evalVector()); @@ -92,7 +87,9 @@ private MethodSpec ctor() { MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC); builder.addParameter(EXPRESSION_EVALUATOR, "field"); builder.addParameter(SOURCE, "source"); + builder.addParameter(DRIVER_CONTEXT, "driverContext"); builder.addStatement("super($N, $N)", "field", "source"); + builder.addStatement("this.driverContext = driverContext"); return builder.build(); } @@ -121,9 +118,9 @@ private MethodSpec evalVector() { { builder.beginControlFlow("try"); { - var constVectType = constantVectorType(resultType); + var constVectType = blockType(resultType); builder.addStatement( - "return new $T($N, positionCount).asBlock()", + "return driverContext.blockFactory().newConstant$TWith($N, positionCount)", constVectType, evalValueCall("vector", "0", scratchPadName) ); @@ -131,59 +128,34 @@ private MethodSpec evalVector() { builder.nextControlFlow("catch (Exception e)"); { builder.addStatement("registerException(e)"); - builder.addStatement("return Block.constantNullBlock(positionCount)"); + builder.addStatement("return Block.constantNullBlock(positionCount, driverContext.blockFactory())"); } builder.endControlFlow(); } builder.endControlFlow(); - builder.addStatement("$T nullsMask = null", BitSet.class); - if (resultType.equals(BYTES_REF)) { - builder.addStatement( - "$T values = new $T(positionCount, $T.NON_RECYCLING_INSTANCE)", // TODO: see note in MvEvaluatorImplementer - BYTES_REF_ARRAY, - BYTES_REF_ARRAY, - BIG_ARRAYS - ); - } else { - builder.addStatement("$T[] values = new $T[positionCount]", resultType, resultType); - } + ClassName returnBlockType = blockType(resultType); + builder.addStatement( + "$T.Builder builder = $T.newBlockBuilder(positionCount, driverContext.blockFactory())", + returnBlockType, + returnBlockType + ); builder.beginControlFlow("for (int p = 0; p < positionCount; p++)"); { builder.beginControlFlow("try"); { - if (resultType.equals(BYTES_REF)) { - builder.addStatement("values.append($N)", evalValueCall("vector", "p", scratchPadName)); - } else { - builder.addStatement("values[p] = $N", evalValueCall("vector", "p", scratchPadName)); - } + builder.addStatement("builder.$L($N)", appendMethod(resultType), evalValueCall("vector", "p", scratchPadName)); } builder.nextControlFlow("catch (Exception e)"); { builder.addStatement("registerException(e)"); - builder.beginControlFlow("if (nullsMask == null)"); - { - builder.addStatement("nullsMask = new BitSet(positionCount)"); - } - builder.endControlFlow(); - builder.addStatement("nullsMask.set(p)"); - if (resultType.equals(BYTES_REF)) { - builder.addStatement("values.append($T.NULL_VALUE)", BYTES_REF_BLOCK); - } + builder.addStatement("builder.appendNull()"); } builder.endControlFlow(); } builder.endControlFlow(); - builder.addStatement( - """ - return nullsMask == null - ? new $T(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new $T(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED)""", - arrayVectorType(resultType), - arrayBlockType(resultType) - ); + builder.addStatement("return builder.build()"); return builder.build(); } @@ -196,7 +168,11 @@ private MethodSpec evalBlock() { builder.addStatement("$T block = ($T) b", blockType, blockType); builder.addStatement("int positionCount = block.getPositionCount()"); TypeName resultBlockType = blockType(resultType); - builder.addStatement("$T.Builder builder = $T.newBlockBuilder(positionCount)", resultBlockType, resultBlockType); + builder.addStatement( + "$T.Builder builder = $T.newBlockBuilder(positionCount, driverContext.blockFactory())", + resultBlockType, + resultBlockType + ); String scratchPadName = null; if (argumentType.equals(BYTES_REF)) { scratchPadName = "scratchPad"; diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java index dba115f4f7c29..0b8b444a2b6a3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayBlock; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; -import org.elasticsearch.compute.data.ConstantBooleanVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToBooleanFromDoubleEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToBooleanFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToBooleanFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBooleanVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBooleanBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - boolean[] values = new boolean[positionCount]; + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendBoolean(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new BooleanArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BooleanArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static boolean evalValue(DoubleVector container, int index) { @@ -72,7 +66,7 @@ private static boolean evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java index d20179fd7baed..1295956645a6f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayBlock; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; -import org.elasticsearch.compute.data.ConstantBooleanVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToBooleanFromIntEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToBooleanFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToBooleanFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBooleanVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBooleanBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - boolean[] values = new boolean[positionCount]; + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendBoolean(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new BooleanArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BooleanArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static boolean evalValue(IntVector container, int index) { @@ -72,7 +66,7 @@ private static boolean evalValue(IntVector container, int index) { public Block evalBlock(Block b) { IntBlock block = (IntBlock) b; int positionCount = block.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java index 7ab2d656a59cb..be01f122f9a8f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayBlock; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; -import org.elasticsearch.compute.data.ConstantBooleanVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToBooleanFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToBooleanFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToBooleanFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBooleanVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBooleanBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - boolean[] values = new boolean[positionCount]; + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendBoolean(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new BooleanArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BooleanArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static boolean evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static boolean evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java index d70d0365aaf4d..7b83995bf0933 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayBlock; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantBooleanVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToBooleanFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToBooleanFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToBooleanFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantBooleanVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBooleanBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - boolean[] values = new boolean[positionCount]; + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendBoolean(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new BooleanArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BooleanArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static boolean evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static boolean evalValue(BytesRefVector container, int index, BytesRef s public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java index d2cf4b41770ce..4a8aebe9cd8ab 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanArrayBlock; -import org.elasticsearch.compute.data.BooleanArrayVector; import org.elasticsearch.compute.data.BooleanBlock; -import org.elasticsearch.compute.data.ConstantBooleanVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToBooleanFromUnsignedLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToBooleanFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToBooleanFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBooleanVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBooleanBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - boolean[] values = new boolean[positionCount]; + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendBoolean(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new BooleanArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BooleanArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static boolean evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static boolean evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount); + BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java index 98310bb390392..ca237c1dcc4a7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToDatetimeFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDatetimeFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDatetimeFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendLong(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static long evalValue(BytesRefVector container, int index, BytesRef scra public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java index a168d93e73ba3..27509a4a18e56 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java @@ -6,14 +6,11 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -22,8 +19,12 @@ * This class is generated. Do not edit it. */ public final class ToDegreesEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDegreesEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDegreesEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -37,29 +38,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(DoubleVector container, int index) { @@ -71,7 +65,7 @@ private static double evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java index d2b16e4b722cb..a6ab12763ddc2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToDoubleFromBooleanEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDoubleFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDoubleFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(BooleanVector container, int index) { @@ -72,7 +66,7 @@ private static double evalValue(BooleanVector container, int index) { public Block evalBlock(Block b) { BooleanBlock block = (BooleanBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java index 53e8edac3c5b3..5889cf151f0fa 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToDoubleFromIntEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDoubleFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDoubleFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(IntVector container, int index) { @@ -72,7 +66,7 @@ private static double evalValue(IntVector container, int index) { public Block evalBlock(Block b) { IntBlock block = (IntBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java index 9be5f1f2456b1..ff1c81f3f544f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToDoubleFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDoubleFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDoubleFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static double evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java index 653034f0c3bc9..197e5e5f2db36 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToDoubleFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDoubleFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDoubleFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendDouble(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static double evalValue(BytesRefVector container, int index, BytesRef sc public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java index 54cc374c758fb..018517ae61d36 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToDoubleFromUnsignedLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToDoubleFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToDoubleFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static double evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java index 76d5c58961970..b62fa771e492c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java @@ -6,17 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -25,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToIPFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIPFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIPFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p, scratchPad)); + builder.appendBytesRef(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(BytesRefVector container, int index, BytesRef public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java index 49f79cd0bcd3e..9529769a02200 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; -import org.elasticsearch.compute.data.ConstantIntVector; -import org.elasticsearch.compute.data.IntArrayBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToIntegerFromBooleanEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIntegerFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIntegerFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantIntVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantIntBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - int[] values = new int[positionCount]; + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendInt(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new IntArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new IntArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static int evalValue(BooleanVector container, int index) { @@ -72,7 +66,7 @@ private static int evalValue(BooleanVector container, int index) { public Block evalBlock(Block b) { BooleanBlock block = (BooleanBlock) b; int positionCount = block.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java index e1b0db72ad7d9..7af8bdbf083ef 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantIntVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; -import org.elasticsearch.compute.data.IntArrayBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToIntegerFromDoubleEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIntegerFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIntegerFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantIntVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantIntBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - int[] values = new int[positionCount]; + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendInt(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new IntArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new IntArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static int evalValue(DoubleVector container, int index) { @@ -72,7 +66,7 @@ private static int evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java index 9a1394b9c02cf..a84367ab27a30 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantIntVector; -import org.elasticsearch.compute.data.IntArrayBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToIntegerFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIntegerFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIntegerFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantIntVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantIntBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - int[] values = new int[positionCount]; + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendInt(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new IntArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new IntArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static int evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static int evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java index 180e64f97e63b..bd7085764e341 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantIntVector; -import org.elasticsearch.compute.data.IntArrayBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToIntegerFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIntegerFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIntegerFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantIntVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantIntBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - int[] values = new int[positionCount]; + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendInt(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new IntArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new IntArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static int evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static int evalValue(BytesRefVector container, int index, BytesRef scrat public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java index 698db22c0ecc6..2312f94fec83e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantIntVector; -import org.elasticsearch.compute.data.IntArrayBlock; -import org.elasticsearch.compute.data.IntArrayVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToIntegerFromUnsignedLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToIntegerFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToIntegerFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantIntVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantIntBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - int[] values = new int[positionCount]; + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendInt(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new IntArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new IntArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static int evalValue(LongVector container, int index) { @@ -72,7 +66,7 @@ private static int evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount); + IntBlock.Builder builder = IntBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java index bf76fb0eb8a59..48e3e45d42f46 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToLongFromBooleanEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToLongFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToLongFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(BooleanVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(BooleanVector container, int index) { public Block evalBlock(Block b) { BooleanBlock block = (BooleanBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java index 116be245e3191..14ec5e41a04e5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToLongFromDoubleEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToLongFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToLongFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(DoubleVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java index 02d043c641cb0..f0eae8bfccc44 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToLongFromIntEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToLongFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToLongFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(IntVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(IntVector container, int index) { public Block evalBlock(Block b) { IntBlock block = (IntBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java index cc825664cc331..8af9a14fd81be 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToLongFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToLongFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToLongFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendLong(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static long evalValue(BytesRefVector container, int index, BytesRef scra public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java index 02bef2f9f9c2d..569df205855d3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java @@ -6,14 +6,11 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -22,8 +19,12 @@ * This class is generated. Do not edit it. */ public final class ToLongFromUnsignedLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToLongFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToLongFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -37,29 +38,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(LongVector container, int index) { @@ -71,7 +65,7 @@ private static long evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java index 33ae94093dd85..6aa373e69b7cd 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java @@ -6,14 +6,11 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantDoubleVector; -import org.elasticsearch.compute.data.DoubleArrayBlock; -import org.elasticsearch.compute.data.DoubleArrayVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -22,8 +19,12 @@ * This class is generated. Do not edit it. */ public final class ToRadiansEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToRadiansEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToRadiansEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -37,29 +38,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantDoubleVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantDoubleBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - double[] values = new double[positionCount]; + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendDouble(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new DoubleArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new DoubleArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static double evalValue(DoubleVector container, int index) { @@ -71,7 +65,7 @@ private static double evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount); + DoubleBlock.Builder builder = DoubleBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java index 876344b1c35bc..8507395c6153a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromBooleanEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(BooleanVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(BooleanVector container, int index) { public Block evalBlock(Block b) { BooleanBlock block = (BooleanBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java index 8aa5148b21de4..7d6bf029fe80b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromDatetimeEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromDatetimeEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromDatetimeEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(LongVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java index 8c7994a3c0a68..e0aa134286723 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromDoubleEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(DoubleVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java index 4e0249939cc91..7ef6c3df27025 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java @@ -6,17 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -25,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromIPEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromIPEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromIPEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p, scratchPad)); + builder.appendBytesRef(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(BytesRefVector container, int index, BytesRef public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java index d076b38f49b91..abe206d5a5152 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromIntEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(IntVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(IntVector container, int index) { public Block evalBlock(Block b) { IntBlock block = (IntBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java index 90448cb992cd2..be6c2648f9eb4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(LongVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java index 91e31c9626b5e..9ba24301875d2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java @@ -6,18 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromUnsignedLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p)); + builder.appendBytesRef(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(LongVector container, int index) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java index 281b881bd6141..69d2e0e106fa0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java @@ -6,17 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -25,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToStringFromVersionEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToStringFromVersionEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToStringFromVersionEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p, scratchPad)); + builder.appendBytesRef(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(BytesRefVector container, int index, BytesRef public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java index ec8b16568c380..541e5b8c7af11 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BooleanVector; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToUnsignedLongFromBooleanEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToUnsignedLongFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToUnsignedLongFromBooleanEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(BooleanVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(BooleanVector container, int index) { public Block evalBlock(Block b) { BooleanBlock block = (BooleanBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java index 2ada365ce848e..89c896ccf1f43 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToUnsignedLongFromDoubleEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToUnsignedLongFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToUnsignedLongFromDoubleEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(DoubleVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(DoubleVector container, int index) { public Block evalBlock(Block b) { DoubleBlock block = (DoubleBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java index 9acad2f9481a6..3c78c24ea7b01 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java @@ -6,15 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -23,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToUnsignedLongFromIntEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToUnsignedLongFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToUnsignedLongFromIntEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -38,29 +39,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(IntVector container, int index) { @@ -72,7 +66,7 @@ private static long evalValue(IntVector container, int index) { public Block evalBlock(Block b) { IntBlock block = (IntBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java index 0cb7da2ed230f..0c0cb9ebfb525 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java @@ -6,14 +6,11 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -22,8 +19,12 @@ * This class is generated. Do not edit it. */ public final class ToUnsignedLongFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToUnsignedLongFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToUnsignedLongFromLongEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -37,29 +38,22 @@ public Block evalVector(Vector v) { int positionCount = v.getPositionCount(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p); + builder.appendLong(evalValue(vector, p)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(LongVector container, int index) { @@ -71,7 +65,7 @@ private static long evalValue(LongVector container, int index) { public Block evalBlock(Block b) { LongBlock block = (LongBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); int start = block.getFirstValueIndex(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java index 3297fcffbe73b..38056be01487c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java @@ -6,16 +6,13 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantLongVector; -import org.elasticsearch.compute.data.LongArrayBlock; -import org.elasticsearch.compute.data.LongArrayVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -24,8 +21,12 @@ * This class is generated. Do not edit it. */ public final class ToUnsignedLongFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToUnsignedLongFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToUnsignedLongFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -40,29 +41,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantLongVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - long[] values = new long[positionCount]; + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values[p] = evalValue(vector, p, scratchPad); + builder.appendLong(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); + builder.appendNull(); } } - return nullsMask == null - ? new LongArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new LongArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static long evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -74,7 +68,7 @@ private static long evalValue(BytesRefVector container, int index, BytesRef scra public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount); + LongBlock.Builder builder = LongBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java index 5f6b62e16de52..bead25f13dd6a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java @@ -6,17 +6,12 @@ import java.lang.Override; import java.lang.String; -import java.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.BytesRefArray; import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BytesRefArrayBlock; -import org.elasticsearch.compute.data.BytesRefArrayVector; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.ConstantBytesRefVector; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.tree.Source; @@ -25,8 +20,12 @@ * This class is generated. Do not edit it. */ public final class ToVersionFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator { - public ToVersionFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source) { + private final DriverContext driverContext; + + public ToVersionFromStringEvaluator(EvalOperator.ExpressionEvaluator field, Source source, + DriverContext driverContext) { super(field, source); + this.driverContext = driverContext; } @Override @@ -41,30 +40,22 @@ public Block evalVector(Vector v) { BytesRef scratchPad = new BytesRef(); if (vector.isConstant()) { try { - return new ConstantBytesRefVector(evalValue(vector, 0, scratchPad), positionCount).asBlock(); + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount); } catch (Exception e) { registerException(e); - return Block.constantNullBlock(positionCount); + return Block.constantNullBlock(positionCount, driverContext.blockFactory()); } } - BitSet nullsMask = null; - BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); for (int p = 0; p < positionCount; p++) { try { - values.append(evalValue(vector, p, scratchPad)); + builder.appendBytesRef(evalValue(vector, p, scratchPad)); } catch (Exception e) { registerException(e); - if (nullsMask == null) { - nullsMask = new BitSet(positionCount); - } - nullsMask.set(p); - values.append(BytesRefBlock.NULL_VALUE); + builder.appendNull(); } } - return nullsMask == null - ? new BytesRefArrayVector(values, positionCount).asBlock() - // UNORDERED, since whatever ordering there is, it isn't necessarily preserved - : new BytesRefArrayBlock(values, positionCount, null, nullsMask, Block.MvOrdering.UNORDERED); + return builder.build(); } private static BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) { @@ -76,7 +67,7 @@ private static BytesRef evalValue(BytesRefVector container, int index, BytesRef public Block evalBlock(Block b) { BytesRefBlock block = (BytesRefBlock) b; int positionCount = block.getPositionCount(); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount); + BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(positionCount, driverContext.blockFactory()); BytesRef scratchPad = new BytesRef(); for (int p = 0; p < positionCount; p++) { int valueCount = block.getValueCount(p); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java index 1af66cb4f50b0..de24b049ea575 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java @@ -9,9 +9,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.data.Vector; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.core.Releasables; @@ -25,7 +27,6 @@ import java.util.Locale; import java.util.Map; -import java.util.function.BiFunction; import java.util.function.Function; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isType; @@ -48,7 +49,7 @@ protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fiel if (evaluator == null) { throw EsqlIllegalArgumentException.illegalDataType(sourceType); } - return dvrCtx -> evaluator.apply(fieldEval.get(dvrCtx), source()); + return dvrCtx -> evaluator.apply(fieldEval.get(dvrCtx), source(), dvrCtx); } @Override @@ -65,7 +66,7 @@ protected final TypeResolution resolveType() { ); } - protected abstract Map> evaluators(); + protected abstract Map> evaluators(); @Override public final Object fold() { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java index 3ec6492ef0d8c..701b3fa67732c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -18,7 +20,6 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.DOUBLE; @@ -30,10 +31,11 @@ public class ToBoolean extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( BOOLEAN, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, KEYWORD, ToBooleanFromStringEvaluator::new, DOUBLE, @@ -51,7 +53,9 @@ public ToBoolean(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java index 5049a80d075f9..eb23e460b88ff 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateParse; import org.elasticsearch.xpack.ql.expression.Expression; @@ -18,7 +20,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; import static org.elasticsearch.xpack.ql.type.DataTypes.DOUBLE; @@ -29,12 +30,13 @@ public class ToDatetime extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( DATETIME, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, LONG, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, KEYWORD, ToDatetimeFromStringEvaluator::new, DOUBLE, @@ -50,7 +52,9 @@ public ToDatetime(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java index ec59446989bca..299e8cfe8643e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java @@ -7,7 +7,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.ql.expression.Expression; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.DOUBLE; import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; @@ -29,16 +30,29 @@ * to degrees. */ public class ToDegrees extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( DOUBLE, ToDegreesEvaluator::new, INTEGER, - (field, source) -> new ToDegreesEvaluator(new ToDoubleFromIntEvaluator(field, source), source), + (field, source, driverContext) -> new ToDegreesEvaluator( + new ToDoubleFromIntEvaluator(field, source, driverContext), + source, + driverContext + ), LONG, - (field, source) -> new ToDegreesEvaluator(new ToDoubleFromLongEvaluator(field, source), source), + (field, source, driverContext) -> new ToDegreesEvaluator( + new ToDoubleFromLongEvaluator(field, source, driverContext), + source, + driverContext + ), UNSIGNED_LONG, - (field, source) -> new ToDegreesEvaluator(new ToDoubleFromUnsignedLongEvaluator(field, source), source) + (field, source, driverContext) -> new ToDegreesEvaluator( + new ToDoubleFromUnsignedLongEvaluator(field, source, driverContext), + source, + driverContext + ) ); public ToDegrees(Source source, Expression field) { @@ -46,7 +60,9 @@ public ToDegrees(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java index dc8527637c7a3..690f7a66cbece 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; @@ -30,10 +31,11 @@ public class ToDouble extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( DOUBLE, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, BOOLEAN, ToDoubleFromBooleanEvaluator::new, DATETIME, @@ -53,7 +55,9 @@ public ToDouble(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java index 0931033758dbb..d55b9d23975e1 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.IP; import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; @@ -25,15 +26,23 @@ public class ToIP extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of(IP, (fieldEval, source) -> fieldEval, KEYWORD, ToIPFromStringEvaluator::new); + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( + IP, + (fieldEval, source, driverContext) -> fieldEval, + KEYWORD, + ToIPFromStringEvaluator::new + ); public ToIP(Source source, Expression field) { super(source, field); } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java index 1d26c4724a423..0fcf62ed3864a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeDoubleToLong; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeToInt; @@ -31,10 +32,11 @@ public class ToInteger extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( INTEGER, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, BOOLEAN, ToIntegerFromBooleanEvaluator::new, DATETIME, @@ -54,7 +56,9 @@ public ToInteger(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java index ffb31a77cb1fc..8e50dd8540ffd 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeDoubleToLong; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeToLong; @@ -32,12 +33,13 @@ public class ToLong extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( LONG, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, DATETIME, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, BOOLEAN, ToLongFromBooleanEvaluator::new, KEYWORD, @@ -55,7 +57,9 @@ public ToLong(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java index 8064303e204d5..8bb5180e09752 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java @@ -7,7 +7,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.ql.expression.Expression; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.DOUBLE; import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; @@ -29,16 +30,29 @@ * to radians. */ public class ToRadians extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( DOUBLE, ToRadiansEvaluator::new, INTEGER, - (field, source) -> new ToRadiansEvaluator(new ToDoubleFromIntEvaluator(field, source), source), + (field, source, driverContext) -> new ToRadiansEvaluator( + new ToDoubleFromIntEvaluator(field, source, driverContext), + source, + driverContext + ), LONG, - (field, source) -> new ToRadiansEvaluator(new ToDoubleFromLongEvaluator(field, source), source), + (field, source, driverContext) -> new ToRadiansEvaluator( + new ToDoubleFromLongEvaluator(field, source, driverContext), + source, + driverContext + ), UNSIGNED_LONG, - (field, source) -> new ToRadiansEvaluator(new ToDoubleFromUnsignedLongEvaluator(field, source), source) + (field, source, driverContext) -> new ToRadiansEvaluator( + new ToDoubleFromUnsignedLongEvaluator(field, source, driverContext), + source, + driverContext + ) ); public ToRadians(Source source, Expression field) { @@ -46,7 +60,9 @@ public ToRadians(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java index 428c1f32b1fc7..af895ab7c56cf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; @@ -21,7 +23,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; @@ -38,10 +39,11 @@ public class ToString extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( KEYWORD, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, BOOLEAN, ToStringFromBooleanEvaluator::new, DATETIME, @@ -55,7 +57,7 @@ public class ToString extends AbstractConvertFunction implements EvaluatorMapper INTEGER, ToStringFromIntEvaluator::new, TEXT, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, VERSION, ToStringFromVersionEvaluator::new, UNSIGNED_LONG, @@ -67,7 +69,9 @@ public ToString(Source source, @Named("v") Expression v) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java index 83deed6b18490..396aa03f39dc6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeToUnsignedLong; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; @@ -33,10 +34,11 @@ public class ToUnsignedLong extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.of( + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.of( UNSIGNED_LONG, - (fieldEval, source) -> fieldEval, + (fieldEval, source, driverContext) -> fieldEval, DATETIME, ToUnsignedLongFromLongEvaluator::new, BOOLEAN, @@ -56,7 +58,9 @@ public ToUnsignedLong(Source source, Expression field) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java index 0051bee45eead..559e2fc4f89fa 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java @@ -8,7 +8,9 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; +import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Named; import org.elasticsearch.xpack.ql.expression.Expression; @@ -19,7 +21,6 @@ import java.util.List; import java.util.Map; -import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; @@ -27,9 +28,10 @@ public class ToVersion extends AbstractConvertFunction { - private static final Map> EVALUATORS = - Map.ofEntries( - Map.entry(VERSION, (fieldEval, source) -> fieldEval), + private static final Map< + DataType, + TriFunction> EVALUATORS = Map.ofEntries( + Map.entry(VERSION, (fieldEval, source, driverContext) -> fieldEval), Map.entry(KEYWORD, ToVersionFromStringEvaluator::new), Map.entry(TEXT, ToVersionFromStringEvaluator::new) ); @@ -39,7 +41,9 @@ public ToVersion(Source source, @Named("v") Expression v) { } @Override - protected Map> evaluators() { + protected + Map> + evaluators() { return EVALUATORS; } From c2072fc4d369d76c2f78024dcc31548ec4789721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Fri, 29 Sep 2023 10:12:07 +0200 Subject: [PATCH 148/155] [DOCS] Adds reference documentation for inference API (#99658) * [DOCS] Creates documentation structure. * [DOCS] Adds PUT inference API docs and part of GET inference API docs. * [DOCS] Fixes complaining CI. * [DOCS] Adds GET and DELETE API docs for inference API. * [DOCS] Adds POST inference API docs. * Apply suggestions from code review --- .../inference/delete-inference.asciidoc | 57 ++++++++++ .../inference/get-inference.asciidoc | 79 +++++++++++++ .../inference/inference-apis.asciidoc | 16 +++ .../inference/post-inference.asciidoc | 97 ++++++++++++++++ .../inference/put-inference.asciidoc | 104 ++++++++++++++++++ docs/reference/rest-api/index.asciidoc | 6 +- 6 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 docs/reference/inference/delete-inference.asciidoc create mode 100644 docs/reference/inference/get-inference.asciidoc create mode 100644 docs/reference/inference/inference-apis.asciidoc create mode 100644 docs/reference/inference/post-inference.asciidoc create mode 100644 docs/reference/inference/put-inference.asciidoc diff --git a/docs/reference/inference/delete-inference.asciidoc b/docs/reference/inference/delete-inference.asciidoc new file mode 100644 index 0000000000000..874bfa64d3551 --- /dev/null +++ b/docs/reference/inference/delete-inference.asciidoc @@ -0,0 +1,57 @@ +[role="xpack"] +[[delete-inference-api]] +=== Delete {infer} API + +Deletes an {infer} model deployment. + + +[discrete] +[[delete-inference-api-request]] +==== {api-request-title} + +`DELETE /_inference//` + +[discrete] +[[delete-inference-api-prereqs]] +==== {api-prereq-title} + +* Requires the `manage` <>. + + +[discrete] +[[delete-inference-api-path-params]] +==== {api-path-parms-title} + +:: +(Required, string) +The unique identifier of the {infer} model to delete. + +:: +(Required, string) +The type of {infer} task that the model performs. + + +[discrete] +[[delete-inference-api-example]] +==== {api-examples-title} + +The following API call deletes the `my-elser-model` {infer} model that can +perform `sparse_embedding` tasks. + + +[source,console] +------------------------------------------------------------ +DELETE /_inference/sparse_embedding/my-elser-model +------------------------------------------------------------ +// TEST[skip:TBD] + + +The API returns the following response: + +[source,console-result] +------------------------------------------------------------ +{ + "acknowledged": true +} +------------------------------------------------------------ +// NOTCONSOLE \ No newline at end of file diff --git a/docs/reference/inference/get-inference.asciidoc b/docs/reference/inference/get-inference.asciidoc new file mode 100644 index 0000000000000..7e32bd05b5f56 --- /dev/null +++ b/docs/reference/inference/get-inference.asciidoc @@ -0,0 +1,79 @@ +[role="xpack"] +[[get-inference-api]] +=== Get {infer} API + +Retrieves {infer} model information. + +[discrete] +[[get-inference-api-request]] +==== {api-request-title} + +`GET /_inference/_all` + +`GET /_inference//_all` + +`GET /_inference//` + +[discrete] +[[get-inference-api-prereqs]] +==== {api-prereq-title} + +* Requires the `manage` <>. + +[discrete] +[[get-inference-api-desc]] +==== {api-description-title} + +You can get information in a single API request for: + +* a single {infer} model by providing the task type and the model ID, +* all of the {infer} models for a certain task type by providing the task type +and a wildcard expression, +* all of the {infer} models by using a wildcard expression. + + +[discrete] +[[get-inference-api-path-params]] +==== {api-path-parms-title} + +``:: +(Optional, string) +The unique identifier of the {infer} model. + + +``:: +(Optional, string) +The type of {infer} task that the model performs. + + +[discrete] +[[get-inference-api-example]] +==== {api-examples-title} + +The following API call retrives information about the `my-elser-model` {infer} +model that can perform `sparse_embedding` tasks. + + +[source,console] +------------------------------------------------------------ +GET _inference/sparse_embedding/my-elser-model +------------------------------------------------------------ +// TEST[skip:TBD] + + +The API returns the following response: + +[source,console-result] +------------------------------------------------------------ +{ + "model_id": "my-elser-model", + "task_type": "sparse_embedding", + "service": "elser_mlnode", + "service_settings": { + "num_allocations": 1, + "num_threads": 1 + }, + "task_settings": {} +} +------------------------------------------------------------ +// NOTCONSOLE \ No newline at end of file diff --git a/docs/reference/inference/inference-apis.asciidoc b/docs/reference/inference/inference-apis.asciidoc new file mode 100644 index 0000000000000..ec1f01bc4d093 --- /dev/null +++ b/docs/reference/inference/inference-apis.asciidoc @@ -0,0 +1,16 @@ +[role="xpack"] +[[inference-apis]] +== {infer-cap} APIs + +You can use the following APIs to manage {infer} models and perform {infer}: + +* <> +* <> +* <> +* <> + + +include::delete-inference.asciidoc[] +include::get-inference.asciidoc[] +include::post-inference.asciidoc[] +include::put-inference.asciidoc[] \ No newline at end of file diff --git a/docs/reference/inference/post-inference.asciidoc b/docs/reference/inference/post-inference.asciidoc new file mode 100644 index 0000000000000..99dd4a059519f --- /dev/null +++ b/docs/reference/inference/post-inference.asciidoc @@ -0,0 +1,97 @@ +[role="xpack"] +[[post-inference-api]] +=== Perform inference API + +Performs an inference task on an input text by using an {infer} model. + + +[discrete] +[[post-inference-api-request]] +==== {api-request-title} + +`POST /_inference//` + + +[discrete] +[[post-inference-api-prereqs]] +==== {api-prereq-title} + +* Requires the `manage` <>. + + +[discrete] +[[post-inference-api-desc]] +==== {api-description-title} + +The perform {infer} API enables you to use {infer} models to perform specific +tasks on data that you provide as an input. The API returns a response with the +resutls of the tasks. The {infer} model you use can perform one specific task +that has been defined when the model was created with the <>. + + +[discrete] +[[post-inference-api-path-params]] +==== {api-path-parms-title} + +``:: +(Required, string) +The unique identifier of the {infer} model. + + +``:: +(Required, string) +The type of {infer} task that the model performs. + + +[discrete] +[[post-inference-api-request-body]] +== {api-request-body-title} + +`input`:: +(Required, string) +The text on which you want to perform the {infer} task. + + +[discrete] +[[post-inference-api-example]] +==== {api-examples-title} + +The following example performs sparse embedding on the example sentence. + + +[source,console] +------------------------------------------------------------ +POST _inference/sparse_embedding/my-elser-model +{ + "input": "The sky above the port was the color of television tuned to a dead channel." +} +------------------------------------------------------------ +// TEST[skip:TBD] + + +The API returns the following response: + + +[source,console-result] +------------------------------------------------------------ +{ + "sparse_embedding": { + "port": 2.1259406, + "sky": 1.7073475, + "color": 1.6922266, + "dead": 1.6247464, + "television": 1.3525393, + "above": 1.2425821, + "tuned": 1.1440028, + "colors": 1.1218185, + "tv": 1.0111054, + "ports": 1.0067928, + "poem": 1.0042328, + "channel": 0.99471164, + "tune": 0.96235967, + "scene": 0.9020516, + (...) + } +} +------------------------------------------------------------ +// NOTCONSOLE \ No newline at end of file diff --git a/docs/reference/inference/put-inference.asciidoc b/docs/reference/inference/put-inference.asciidoc new file mode 100644 index 0000000000000..c5ccd6a57a8dd --- /dev/null +++ b/docs/reference/inference/put-inference.asciidoc @@ -0,0 +1,104 @@ +[role="xpack"] +[[put-inference-api]] +=== Create {infer} API + +Creates a model to perform an {infer} task. + + +[discrete] +[[put-inference-api-request]] +==== {api-request-title} + +`PUT /_inference//` + + +[discrete] +[[put-inference-api-prereqs]] +==== {api-prereq-title} + +* Requires the `manage` <>. + +[discrete] +[[put-inference-api-desc]] +==== {api-description-title} + +The create {infer} API enables you to create and configure an {infer} model to +perform a specific {infer} task. + + +[discrete] +[[put-inference-api-path-params]] +==== {api-path-parms-title} + + +``:: +(Required, string) +The unique identifier of the model. + +``:: +(Required, string) +The type of the {infer} task that the model will perform. Available task types: +* `sparse_embedding`, +* `text_embedding`. + + +[discrete] +[[put-inference-api-request-body]] +== {api-request-body-title} + +`service`:: +(Required, string) +The type of service supported for the specified task type. +Available services: +* `elser`, +* `elser_mlnode`. + +`service_settings`:: +(Required, object) +Settings used to install the {infer} model. These settings are specific to the +`service` you specified. + +`task_settings`:: +(Optional, object) +Settings to configure the {infer} task. These settings are specific to the +`` you specified. + + +[discrete] +[[put-inference-api-example]] +==== {api-examples-title} + +The following example shows how to create an {infer} model called +`my-elser-model` to perform a `sparse_embedding` task type. + +[source,console] +------------------------------------------------------------ +PUT _inference/sparse_embedding/my-elser-model +{ + "service": "elser_mlnode", + "service_settings": { + "num_allocations": 1, + "num_threads": 1 + }, + "task_settings": {} +} +------------------------------------------------------------ +// TEST[skip:TBD] + + +Example response: + +[source,console-result] +------------------------------------------------------------ +{ + "model_id": "my-elser-model", + "task_type": "sparse_embedding", + "service": "elser_mlnode", + "service_settings": { + "num_allocations": 1, + "num_threads": 1 + }, + "task_settings": {} +} +------------------------------------------------------------ +// NOTCONSOLE diff --git a/docs/reference/rest-api/index.asciidoc b/docs/reference/rest-api/index.asciidoc index 1da39333db43e..b8ad9d9a0736e 100644 --- a/docs/reference/rest-api/index.asciidoc +++ b/docs/reference/rest-api/index.asciidoc @@ -28,8 +28,9 @@ not be included yet. * <> * <> * <> -* <> +* <> * <> +* <> * <> * <> * <> @@ -74,8 +75,9 @@ include::{es-repo-dir}/text-structure/apis/find-structure.asciidoc[leveloffset=+ include::{es-repo-dir}/graph/explore.asciidoc[] include::{es-repo-dir}/indices.asciidoc[] include::{es-repo-dir}/ilm/apis/ilm-api.asciidoc[] -include::{es-repo-dir}/ingest/apis/index.asciidoc[] +include::{es-repo-dir}/inference/inference-apis.asciidoc[] include::info.asciidoc[] +include::{es-repo-dir}/ingest/apis/index.asciidoc[] include::{es-repo-dir}/licensing/index.asciidoc[] include::{es-repo-dir}/rest-api/logstash/index.asciidoc[] include::{es-repo-dir}/ml/common/apis/index.asciidoc[] From e23fd3269c07e322f0fd112582edb6c131f93066 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 29 Sep 2023 09:45:33 +0100 Subject: [PATCH 149/155] Move SLM eligibility check (#100044) A small refactoring to make #99953 a little simpler: combine the logic for retrieving the snapshot info and filtering out the ineligible ones into a single function so we can replace it with a call to a dedicated client action in a followup. --- .../xpack/slm/SnapshotRetentionTask.java | 74 ++++++++++--------- .../xpack/slm/SnapshotRetentionTaskTests.java | 60 ++++++--------- 2 files changed, 63 insertions(+), 71 deletions(-) diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java index ac1b2f30ec06d..708621ee4a6c8 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java @@ -91,15 +91,6 @@ public SnapshotRetentionTask( this.historyStore = historyStore; } - private static String formatSnapshots(Map> snapshotMap) { - return snapshotMap.entrySet() - .stream() - .map( - e -> e.getKey() + ": [" + e.getValue().stream().map(si -> si.snapshotId().getName()).collect(Collectors.joining(",")) + "]" - ) - .collect(Collectors.joining(",")); - } - @Override public void triggered(SchedulerEngine.Event event) { assert event.getJobName().equals(SnapshotRetentionService.SLM_RETENTION_JOB_ID) @@ -156,28 +147,9 @@ public void triggered(SchedulerEngine.Event event) { // Finally, asynchronously retrieve all the snapshots, deleting them serially, // before updating the cluster state with the new metrics and setting 'running' // back to false - getAllRetainableSnapshots(repositioriesToFetch, policiesWithRetention.keySet(), new ActionListener<>() { + getSnapshotsEligibleForDeletion(repositioriesToFetch, policiesWithRetention, new ActionListener<>() { @Override - public void onResponse(Map> allSnapshots) { - if (logger.isTraceEnabled()) { - logger.trace("retrieved snapshots: [{}]", formatSnapshots(allSnapshots)); - } - // Find all the snapshots that are past their retention date - final Map>> snapshotsToBeDeleted = allSnapshots.entrySet() - .stream() - .collect( - Collectors.toMap( - Map.Entry::getKey, - e -> e.getValue() - .stream() - .filter(snapshot -> snapshotEligibleForDeletion(snapshot, allSnapshots, policiesWithRetention)) - // SnapshotInfo instances can be quite large in case they contain e.g. a large collection of - // exceptions so we extract the only two things (id + policy id) here so they can be GCed - .map(snapshotInfo -> Tuple.tuple(snapshotInfo.snapshotId(), getPolicyId(snapshotInfo))) - .toList() - ) - ); - + public void onResponse(Map>> snapshotsToBeDeleted) { if (logger.isTraceEnabled()) { logger.trace("snapshots eligible for deletion: [{}]", snapshotsToBeDeleted); } @@ -256,10 +228,10 @@ static boolean snapshotEligibleForDeletion( return eligible; } - void getAllRetainableSnapshots( + void getSnapshotsEligibleForDeletion( Collection repositories, - Set policies, - ActionListener>> listener + Map policies, + ActionListener>>> listener ) { if (repositories.isEmpty()) { // Skip retrieving anything if there are no repositories to fetch @@ -273,7 +245,7 @@ void getAllRetainableSnapshots( // don't time out on this request to not produce failed SLM runs in case of a temporarily slow master node .setMasterNodeTimeout(TimeValue.MAX_VALUE) .setIgnoreUnavailable(true) - .setPolicies(policies.toArray(Strings.EMPTY_ARRAY)) + .setPolicies(policies.keySet().toArray(Strings.EMPTY_ARRAY)) .setIncludeIndexNames(false) .execute(ActionListener.wrap(resp -> { if (logger.isTraceEnabled()) { @@ -300,7 +272,39 @@ void getAllRetainableSnapshots( logger.debug(() -> "unable to retrieve snapshots for [" + repo + "] repositories: ", resp.getFailures().get(repo)); } } - listener.onResponse(snapshots); + + if (logger.isTraceEnabled()) { + logger.trace( + "retrieved snapshots: [{}]", + snapshots.entrySet() + .stream() + .map( + e -> e.getKey() + + ": [" + + e.getValue().stream().map(si -> si.snapshotId().getName()).collect(Collectors.joining(",")) + + "]" + ) + .collect(Collectors.joining(",")) + ); + } + + // Find all the snapshots that are past their retention date + final Map>> snapshotsToBeDeleted = snapshots.entrySet() + .stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue() + .stream() + .filter(snapshot -> snapshotEligibleForDeletion(snapshot, snapshots, policies)) + // SnapshotInfo instances can be quite large in case they contain e.g. a large collection of + // exceptions so we extract the only two things (id + policy id) here so they can be GCed + .map(snapshotInfo -> Tuple.tuple(snapshotInfo.snapshotId(), getPolicyId(snapshotInfo))) + .toList() + ) + ); + + listener.onResponse(snapshotsToBeDeleted); }, e -> { logger.debug(() -> "unable to retrieve snapshots for [" + repositories + "] repositories: ", e); listener.onFailure(e); diff --git a/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SnapshotRetentionTaskTests.java b/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SnapshotRetentionTaskTests.java index 15badabf3689a..b120b49c63654 100644 --- a/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SnapshotRetentionTaskTests.java +++ b/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SnapshotRetentionTaskTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.core.Tuple; import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotInfo; @@ -42,7 +43,6 @@ import org.elasticsearch.xpack.core.slm.SnapshotRetentionConfiguration; import org.elasticsearch.xpack.slm.history.SnapshotHistoryStore; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -238,20 +238,6 @@ private void retentionTaskTest(final boolean deletionSuccess) throws Exception { 0L, Collections.emptyMap() ); - final SnapshotInfo ineligibleSnapshot = new SnapshotInfo( - new Snapshot(repoId, new SnapshotId("name2", "uuid2")), - Collections.singletonList("index"), - Collections.emptyList(), - Collections.emptyList(), - null, - System.currentTimeMillis() + 1, - 1, - Collections.emptyList(), - true, - Collections.singletonMap("policy", policyId), - System.currentTimeMillis(), - Collections.emptyMap() - ); Set deleted = ConcurrentHashMap.newKeySet(); Set deletedSnapshotsInHistory = ConcurrentHashMap.newKeySet(); @@ -273,11 +259,9 @@ private void retentionTaskTest(final boolean deletionSuccess) throws Exception { historyLatch.countDown(); }), () -> { - List snaps = new ArrayList<>(2); - snaps.add(eligibleSnapshot); - snaps.add(ineligibleSnapshot); - logger.info("--> retrieving snapshots [{}]", snaps); - return Collections.singletonMap(repoId, snaps); + final var result = Collections.singletonMap(repoId, List.of(Tuple.tuple(eligibleSnapshot.snapshotId(), policyId))); + logger.info("--> retrieving snapshots [{}]", result); + return result; }, (deletionPolicyId, repo, snapId, slmStats, listener) -> { logger.info("--> deleting {} from repo {}", snapId, repo); @@ -295,7 +279,7 @@ private void retentionTaskTest(final boolean deletionSuccess) throws Exception { long time = System.currentTimeMillis(); retentionTask.triggered(new SchedulerEngine.Event(SnapshotRetentionService.SLM_RETENTION_JOB_ID, time, time)); - deletionLatch.await(10, TimeUnit.SECONDS); + safeAwait(deletionLatch); assertThat("something should have been deleted", deleted, not(empty())); assertThat("one snapshot should have been deleted", deleted, hasSize(1)); @@ -364,18 +348,22 @@ protected void ); AtomicReference errHandlerCalled = new AtomicReference<>(null); - task.getAllRetainableSnapshots(Collections.singleton(repoId), Collections.singleton(policyId), new ActionListener<>() { - @Override - public void onResponse(Map> stringListMap) { - logger.info("--> forcing failure"); - throw new ElasticsearchException("forced failure"); - } + task.getSnapshotsEligibleForDeletion( + Collections.singleton(repoId), + Map.of(policyId, new SnapshotLifecyclePolicy(policyId, "test", "* * * * *", repoId, null, null)), + new ActionListener<>() { + @Override + public void onResponse(Map>> snapshotsToBeDeleted) { + logger.info("--> forcing failure"); + throw new ElasticsearchException("forced failure"); + } - @Override - public void onFailure(Exception e) { - errHandlerCalled.set(e); + @Override + public void onFailure(Exception e) { + errHandlerCalled.set(e); + } } - }); + ); assertNotNull(errHandlerCalled.get()); assertThat(errHandlerCalled.get().getMessage(), equalTo("forced failure")); @@ -597,14 +585,14 @@ public ClusterState createState(OperationMode mode, SnapshotLifecyclePolicy... p } private static class MockSnapshotRetentionTask extends SnapshotRetentionTask { - private final Supplier>> snapshotRetriever; + private final Supplier>>> snapshotRetriever; private final DeleteSnapshotMock deleteRunner; MockSnapshotRetentionTask( Client client, ClusterService clusterService, SnapshotHistoryStore historyStore, - Supplier>> snapshotRetriever, + Supplier>>> snapshotRetriever, DeleteSnapshotMock deleteRunner, LongSupplier nanoSupplier ) { @@ -614,10 +602,10 @@ private static class MockSnapshotRetentionTask extends SnapshotRetentionTask { } @Override - void getAllRetainableSnapshots( + void getSnapshotsEligibleForDeletion( Collection repositories, - Set policies, - ActionListener>> listener + Map policies, + ActionListener>>> listener ) { listener.onResponse(this.snapshotRetriever.get()); } From fd65b6193b1463fcca24aa8e492c827e83937c1d Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Fri, 29 Sep 2023 10:56:01 +0200 Subject: [PATCH 150/155] Make MappedFieldType#meta type "more consistent" (#100041) We use various types for this field depending on the code path taken. This leads to a rather high cost for serializing the meta map in field caps (from the virtual call overhead as well as the fact that we have an unmodifiable wrapped `TreeMap` here quiet often). => this PR makes a bit of an effort to use a consistent type as much as possible to get more predictable performance. --- .../action/fieldcaps/FieldCapabilitiesFetcher.java | 2 +- .../action/fieldcaps/FieldCapabilitiesIndexResponse.java | 8 -------- .../action/fieldcaps/IndexFieldCapabilities.java | 2 +- .../java/org/elasticsearch/index/mapper/FieldMapper.java | 3 +-- .../org/elasticsearch/index/mapper/MappedFieldType.java | 4 +++- .../java/org/elasticsearch/index/mapper/TypeParsers.java | 9 +++++++++ 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java index 7dc73940ce2ff..969d86f5f470c 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesFetcher.java @@ -178,7 +178,7 @@ static Map retrieveFieldCaps( false, false, null, - Collections.emptyMap() + Map.of() ); responseMap.put(parentField, fieldCap); } diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java index e9e3a05169afc..06ea2dee17481 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java @@ -203,14 +203,6 @@ public Map get() { return responseMap; } - /** - * - * Get the field capabilities for the provided {@code field} - */ - public IndexFieldCapabilities getField(String field) { - return responseMap.get(field); - } - TransportVersion getOriginVersion() { return originVersion; } diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java index ef609a06cb8be..de2f6965e011d 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/IndexFieldCapabilities.java @@ -63,7 +63,7 @@ public static IndexFieldCapabilities readFrom(StreamInput in) throws IOException isAggregatable, isDimension, metricType, - in.readMap(StreamInput::readString) + in.readImmutableMap(StreamInput::readString) ); } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java index a9d90f80c8a18..350ac22c5e216 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java @@ -36,7 +36,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; @@ -1108,7 +1107,7 @@ public static Parameter> metaParam() { return new Parameter<>( "meta", true, - Collections::emptyMap, + Map::of, (n, c, o) -> TypeParsers.parseMeta(n, o), m -> m.fieldType().meta(), XContentBuilder::stringStringMap, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index d7fa0dae21b38..21ed56a82292c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -79,7 +79,9 @@ public MappedFieldType( this.isStored = isStored; this.docValues = hasDocValues; this.textSearchInfo = Objects.requireNonNull(textSearchInfo); - this.meta = Objects.requireNonNull(meta); + // meta should be sorted but for the one item or empty case we can fall back to immutable maps to save some memory since order is + // irrelevant + this.meta = meta.size() <= 1 ? Map.copyOf(meta) : meta; } /** diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java b/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java index c42c4df01c5fa..40c96b9976317 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java @@ -38,6 +38,9 @@ public static Map parseMeta(String name, Object metaObject) { } @SuppressWarnings("unchecked") Map meta = (Map) metaObject; + if (meta.isEmpty()) { + return Map.of(); + } if (meta.size() > 5) { throw new MapperParsingException("[meta] can't have more than 5 entries, but got " + meta.size() + " on field [" + name + "]"); } @@ -69,6 +72,12 @@ public static Map parseMeta(String name, Object metaObject) { ); } } + var entrySet = meta.entrySet(); + if (entrySet.size() == 1) { + // no need to sort for a single entry + var entry = entrySet.iterator().next(); + return Map.of(entry.getKey(), (String) entry.getValue()); + } Map sortedMeta = new TreeMap<>(); for (Map.Entry entry : meta.entrySet()) { sortedMeta.put(entry.getKey(), (String) entry.getValue()); From 98b9e819eefa30d37956e0ee609453723a9b1d08 Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Fri, 29 Sep 2023 12:30:55 +0300 Subject: [PATCH 151/155] Represent histogram value count as long (#99912) * Represent histogram value count as long Histograms currently use integers to store the count of each value, which can overflow. Switch to using long integers to avoid this. TDigestState was updated to use long for centroid value count in #99491 Fixes #99820 * Update docs/changelog/99912.yaml * spotless fix --- docs/changelog/99912.yaml | 6 ++ .../mapping/types/histogram.asciidoc | 4 +- .../org/elasticsearch/TransportVersions.java | 1 + .../index/fielddata/HistogramValue.java | 2 +- .../HistoBackedHistogramAggregator.java | 2 +- .../range/HistoBackedRangeAggregator.java | 8 +-- .../mapper/HistogramFieldMapper.java | 23 +++++--- .../mapper/HistogramFieldMapperTests.java | 4 +- .../xpack/downsample/LabelFieldProducer.java | 2 +- .../test/analytics/histogram.yml | 55 +++++++++++++++++++ 10 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 docs/changelog/99912.yaml diff --git a/docs/changelog/99912.yaml b/docs/changelog/99912.yaml new file mode 100644 index 0000000000000..06f0f9baa9661 --- /dev/null +++ b/docs/changelog/99912.yaml @@ -0,0 +1,6 @@ +pr: 99912 +summary: Represent histogram value count as long +area: Aggregations +type: enhancement +issues: + - 99820 diff --git a/docs/reference/mapping/types/histogram.asciidoc b/docs/reference/mapping/types/histogram.asciidoc index 70164dba236ce..38887cef013b9 100644 --- a/docs/reference/mapping/types/histogram.asciidoc +++ b/docs/reference/mapping/types/histogram.asciidoc @@ -10,7 +10,7 @@ This data is defined using two paired arrays: * A `values` array of <> numbers, representing the buckets for the histogram. These values must be provided in ascending order. -* A corresponding `counts` array of <> numbers, representing how +* A corresponding `counts` array of <> numbers, representing how many values fall into each bucket. These numbers must be positive or zero. Because the elements in the `values` array correspond to the elements in the @@ -138,5 +138,5 @@ PUT my-index-000001/_doc/2 <1> Values for each bucket. Values in the array are treated as doubles and must be given in increasing order. For <> histograms this value represents the mean value. In case of HDR histograms this represents the value iterated to. -<2> Count for each bucket. Values in the arrays are treated as integers and must be positive or zero. +<2> Count for each bucket. Values in the arrays are treated as long integers and must be positive or zero. Negative values will be rejected. The relation between a bucket and a count is given by the position in the array. diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 30cff2ce427be..b460c7aaf2ebc 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -148,6 +148,7 @@ static TransportVersion def(int id) { public static final TransportVersion COMPACT_FIELD_CAPS_ADDED = def(8_505_00_0); public static final TransportVersion DATA_STREAM_RESPONSE_INDEX_PROPERTIES = def(8_506_00_0); public static final TransportVersion ML_TRAINED_MODEL_CONFIG_PLATFORM_ADDED = def(8_507_00_0); + public static final TransportVersion LONG_COUNT_IN_HISTOGRAM_ADDED = def(8_508_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/HistogramValue.java b/server/src/main/java/org/elasticsearch/index/fielddata/HistogramValue.java index 902246b442e3b..3ee868ed85ee2 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/HistogramValue.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/HistogramValue.java @@ -32,6 +32,6 @@ public abstract class HistogramValue { * The current count of the histogram * @return the current count of the histogram */ - public abstract int count(); + public abstract long count(); } diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java index 2e76da1a519bd..88e039b6013e4 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/histogram/HistoBackedHistogramAggregator.java @@ -88,7 +88,7 @@ public void collect(int doc, long owningBucketOrd) throws IOException { double previousKey = Double.NEGATIVE_INFINITY; while (sketch.next()) { final double value = sketch.value(); - final int count = sketch.count(); + final long count = sketch.count(); double key = Math.floor((value - offset) / interval); assert key >= previousKey; diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java index b2be65a9e2901..2db972115767e 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/bucket/range/HistoBackedRangeAggregator.java @@ -134,7 +134,7 @@ public void collect(int doc, long bucket) throws IOException { previousValue = value; // Collecting the bucket automatically increments the count by the docCountProvider, // account for that here - final int count = sketch.count() - docCountProvider.getDocCount(doc); + final long count = sketch.count() - docCountProvider.getDocCount(doc); lo = HistoBackedRangeAggregator.this.collect(sub, doc, value, bucket, lo, count); } } @@ -142,7 +142,7 @@ public void collect(int doc, long bucket) throws IOException { }; } - abstract int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, int count) + abstract int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, long count) throws IOException; private static class NoOverlap extends HistoBackedRangeAggregator { @@ -178,7 +178,7 @@ private NoOverlap( } @Override - public int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, int count) + public int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, long count) throws IOException { int lo = lowBound, hi = ranges.length - 1; while (lo <= hi) { @@ -240,7 +240,7 @@ private static class Overlap extends HistoBackedRangeAggregator { } @Override - public int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, int count) + public int collect(LeafBucketCollector sub, int doc, double value, long owningBucketOrdinal, int lowBound, long count) throws IOException { int lo = lowBound, hi = ranges.length - 1; // all candidates are between these indexes int mid = (lo + hi) >>> 1; diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java index 08fad5dd3b83c..a06eb509d2539 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java @@ -15,6 +15,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.SortField; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.TransportVersions; import org.elasticsearch.common.Explicit; import org.elasticsearch.common.io.stream.ByteArrayStreamInput; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -295,7 +296,7 @@ public void parse(DocumentParserContext context) throws IOException { return; } ArrayList values = null; - ArrayList counts = null; + ArrayList counts = null; // should be an object ensureExpectedToken(XContentParser.Token.START_OBJECT, token, context.parser()); subParser = new XContentSubParser(context.parser()); @@ -343,7 +344,7 @@ public void parse(DocumentParserContext context) throws IOException { while (token != XContentParser.Token.END_ARRAY) { // should be a number ensureExpectedToken(XContentParser.Token.VALUE_NUMBER, token, subParser); - counts.add(subParser.intValue()); + counts.add(subParser.longValue()); token = subParser.nextToken(); } } else { @@ -385,7 +386,7 @@ public void parse(DocumentParserContext context) throws IOException { } BytesStreamOutput streamOutput = new BytesStreamOutput(); for (int i = 0; i < values.size(); i++) { - int count = counts.get(i); + long count = counts.get(i); if (count < 0) { throw new DocumentParsingException( subParser.getTokenLocation(), @@ -393,7 +394,11 @@ public void parse(DocumentParserContext context) throws IOException { ); } else if (count > 0) { // we do not add elements with count == 0 - streamOutput.writeVInt(count); + if (streamOutput.getTransportVersion().onOrAfter(TransportVersions.LONG_COUNT_IN_HISTOGRAM_ADDED)) { + streamOutput.writeVLong(count); + } else { + streamOutput.writeVInt(Math.toIntExact(count)); + } streamOutput.writeLong(Double.doubleToRawLongBits(values.get(i))); } } @@ -431,7 +436,7 @@ public void parse(DocumentParserContext context) throws IOException { /** re-usable {@link HistogramValue} implementation */ private static class InternalHistogramValue extends HistogramValue { double value; - int count; + long count; boolean isExhausted; final ByteArrayStreamInput streamInput; @@ -450,7 +455,11 @@ void reset(BytesRef bytesRef) { @Override public boolean next() throws IOException { if (streamInput.available() > 0) { - count = streamInput.readVInt(); + if (streamInput.getTransportVersion().onOrAfter(TransportVersions.LONG_COUNT_IN_HISTOGRAM_ADDED)) { + count = streamInput.readVLong(); + } else { + count = streamInput.readVInt(); + } value = Double.longBitsToDouble(streamInput.readLong()); return true; } @@ -467,7 +476,7 @@ public double value() { } @Override - public int count() { + public long count() { if (isExhausted) { throw new IllegalArgumentException("histogram already exhausted"); } diff --git a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapperTests.java b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapperTests.java index 9be00cacd0e09..2892ada15fec9 100644 --- a/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapperTests.java +++ b/x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapperTests.java @@ -281,8 +281,8 @@ public void testCountIsLong() throws Exception { .field("values", new double[] { 2, 2, 3 }) .endObject() ); - Exception e = expectThrows(DocumentParsingException.class, () -> mapper.parse(source)); - assertThat(e.getCause().getMessage(), containsString(" out of range of int")); + ParsedDocument doc = mapper.parse(source); + assertThat(doc.rootDoc().getField("field"), notNullValue()); } public void testValuesNotInOrder() throws Exception { diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/LabelFieldProducer.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/LabelFieldProducer.java index c2b8f909618b7..013ad20ffe04d 100644 --- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/LabelFieldProducer.java +++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/LabelFieldProducer.java @@ -158,7 +158,7 @@ public void write(XContentBuilder builder) throws IOException { if (isEmpty() == false) { final HistogramValue histogramValue = (HistogramValue) label.get(); final List values = new ArrayList<>(); - final List counts = new ArrayList<>(); + final List counts = new ArrayList<>(); while (histogramValue.next()) { values.add(histogramValue.value()); counts.add(histogramValue.count()); diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/analytics/histogram.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/analytics/histogram.yml index 7c1d99458291f..b2f710e5ffed8 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/analytics/histogram.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/analytics/histogram.yml @@ -251,3 +251,58 @@ histogram with synthetic source and zero counts: latency: values: [0.2, 0.4] counts: [7, 6] + + +--- +histogram with large count values: + - skip: + version: " - 8.10.99" + reason: Support for `long` values was introduced in 8.11.0 + + - do: + indices.create: + index: histo_large_count + body: + mappings: + properties: + latency: + type: histogram + - do: + bulk: + index: histo_large_count + refresh: true + body: + - '{"index": {}}' + - '{"latency": {"values" : [0.1, 0.2, 0.3, 0.4, 0.5], "counts" : [0, 1000000000000, 10, 1000, 1000000]}}' + + - do: + search: + index: histo_large_count + body: + size: 0 + aggs: + histo: + histogram: + field: latency + interval: 0.3 + + - length: { aggregations.histo.buckets: 2 } + - match: { aggregations.histo.buckets.0.key: 0.0 } + - match: { aggregations.histo.buckets.0.doc_count: 1000000000000 } + - match: { aggregations.histo.buckets.1.key: 0.3 } + - match: { aggregations.histo.buckets.1.doc_count: 1001010 } + + - do: + search: + index: histo_large_count + body: + size: 0 + aggs: + percent: + percentiles: + field: latency + + - length: { aggregations.percent.values: 7 } + - match: { aggregations.percent.values.1\.0: 0.2 } + - match: { aggregations.percent.values.5\.0: 0.2 } + - match: { aggregations.percent.values.25\.0: 0.2 } From 567ab3a29996dc31d3c8039a74309346f72669d8 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 29 Sep 2023 12:09:17 +0100 Subject: [PATCH 152/155] Inline SnapshotRetentionConfiguration#getSnapshotDeletionPredicate (#100053) We only ever test each instance of this predicate once, immediately after creating it, so we may as well just convert it into a regular method that returns `boolean` instead. More preliminary work before fixing #99953 --- .../slm/SnapshotRetentionConfiguration.java | 243 +++++++++--------- .../SnapshotRetentionConfigurationTests.java | 108 ++++---- .../xpack/slm/SnapshotRetentionTask.java | 5 +- 3 files changed, 177 insertions(+), 179 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfiguration.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfiguration.java index 54c153c04ea31..544fcfec822dd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfiguration.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfiguration.java @@ -30,7 +30,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.LongSupplier; -import java.util.function.Predicate; import static org.elasticsearch.core.Strings.format; @@ -125,11 +124,10 @@ public Integer getMaximumSnapshotCount() { } /** - * Return a predicate by which a SnapshotInfo can be tested to see - * whether it should be deleted according to this retention policy. + * @return whether a SnapshotInfo should be deleted according to this retention policy. * @param allSnapshots a list of all snapshot pertaining to this SLM policy and repository */ - public Predicate getSnapshotDeletionPredicate(final List allSnapshots) { + public boolean isSnapshotEligibleForDeletion(SnapshotInfo si, List allSnapshots) { final int totalSnapshotCount = allSnapshots.size(); final List sortedSnapshots = allSnapshots.stream().sorted(Comparator.comparingLong(SnapshotInfo::startTime)).toList(); int successCount = 0; @@ -142,139 +140,138 @@ public Predicate getSnapshotDeletionPredicate(final List { - final String snapName = si.snapshotId().getName(); - // First, if there's no expire_after and a more recent successful snapshot, we can delete all the failed ones - if (this.expireAfter == null && UNSUCCESSFUL_STATES.contains(si.state()) && newestSuccessfulTimestamp > si.startTime()) { - // There's no expire_after and there's a more recent successful snapshot, delete this failed one - logger.trace("[{}]: ELIGIBLE as it is {} and there is a more recent successful snapshot", snapName, si.state()); - return true; - } + final String snapName = si.snapshotId().getName(); - // Next, enforce the maximum count, if the size is over the maximum number of - // snapshots, then allow the oldest N (where N is the number over the maximum snapshot - // count) snapshots to be eligible for deletion - if (this.maximumSnapshotCount != null && successfulSnapshotCount > this.maximumSnapshotCount) { - final long successfulSnapsToDelete = successfulSnapshotCount - this.maximumSnapshotCount; - boolean found = false; - int successfulSeen = 0; - for (SnapshotInfo s : sortedSnapshots) { - if (s.state() == SnapshotState.SUCCESS) { - successfulSeen++; - } - if (successfulSeen > successfulSnapsToDelete) { - break; - } - if (s.equals(si)) { - found = true; - break; - } + // First, if there's no expire_after and a more recent successful snapshot, we can delete all the failed ones + if (this.expireAfter == null && UNSUCCESSFUL_STATES.contains(si.state()) && newestSuccessfulTimestamp > si.startTime()) { + // There's no expire_after and there's a more recent successful snapshot, delete this failed one + logger.trace("[{}]: ELIGIBLE as it is {} and there is a more recent successful snapshot", snapName, si.state()); + return true; + } + + // Next, enforce the maximum count, if the size is over the maximum number of + // snapshots, then allow the oldest N (where N is the number over the maximum snapshot + // count) snapshots to be eligible for deletion + if (this.maximumSnapshotCount != null && successfulSnapshotCount > this.maximumSnapshotCount) { + final long successfulSnapsToDelete = successfulSnapshotCount - this.maximumSnapshotCount; + boolean found = false; + int successfulSeen = 0; + for (SnapshotInfo s : sortedSnapshots) { + if (s.state() == SnapshotState.SUCCESS) { + successfulSeen++; } - if (found) { - logger.trace( - "[{}]: ELIGIBLE as it is one of the {} oldest snapshots with " - + "{} non-failed snapshots ({} total), over the limit of {} maximum snapshots", - snapName, - successfulSnapsToDelete, - successfulSnapshotCount, - totalSnapshotCount, - this.maximumSnapshotCount - ); - return true; - } else { - logger.trace( - "[{}]: SKIPPING as it is not one of the {} oldest snapshots with " - + "{} non-failed snapshots ({} total), over the limit of {} maximum snapshots", - snapName, - successfulSnapsToDelete, - successfulSnapshotCount, - totalSnapshotCount, - this.maximumSnapshotCount - ); + if (successfulSeen > successfulSnapsToDelete) { + break; + } + if (s.equals(si)) { + found = true; + break; } } + if (found) { + logger.trace( + "[{}]: ELIGIBLE as it is one of the {} oldest snapshots with " + + "{} non-failed snapshots ({} total), over the limit of {} maximum snapshots", + snapName, + successfulSnapsToDelete, + successfulSnapshotCount, + totalSnapshotCount, + this.maximumSnapshotCount + ); + return true; + } else { + logger.trace( + "[{}]: SKIPPING as it is not one of the {} oldest snapshots with " + + "{} non-failed snapshots ({} total), over the limit of {} maximum snapshots", + snapName, + successfulSnapsToDelete, + successfulSnapshotCount, + totalSnapshotCount, + this.maximumSnapshotCount + ); + } + } - // Next check the minimum count, since that is a blanket requirement regardless of time, - // if we haven't hit the minimum then we need to keep the snapshot regardless of - // expiration time - if (this.minimumSnapshotCount != null && successfulSnapshotCount <= this.minimumSnapshotCount) { - if (UNSUCCESSFUL_STATES.contains(si.state()) == false) { - logger.trace( - "[{}]: INELIGIBLE as there are {} non-failed snapshots ({} total) and {} minimum snapshots needed", - snapName, - successfulSnapshotCount, - totalSnapshotCount, - this.minimumSnapshotCount - ); - return false; - } else { - logger.trace( - "[{}]: SKIPPING minimum snapshot count check as this snapshot is {} and not counted " - + "towards the minimum snapshot count.", - snapName, - si.state() - ); - } + // Next check the minimum count, since that is a blanket requirement regardless of time, + // if we haven't hit the minimum then we need to keep the snapshot regardless of + // expiration time + if (this.minimumSnapshotCount != null && successfulSnapshotCount <= this.minimumSnapshotCount) { + if (UNSUCCESSFUL_STATES.contains(si.state()) == false) { + logger.trace( + "[{}]: INELIGIBLE as there are {} non-failed snapshots ({} total) and {} minimum snapshots needed", + snapName, + successfulSnapshotCount, + totalSnapshotCount, + this.minimumSnapshotCount + ); + return false; + } else { + logger.trace( + "[{}]: SKIPPING minimum snapshot count check as this snapshot is {} and not counted " + + "towards the minimum snapshot count.", + snapName, + si.state() + ); } + } - // Finally, check the expiration time of the snapshot, if it is past, then it is - // eligible for deletion - if (this.expireAfter != null) { - if (this.minimumSnapshotCount != null) { - // Only the oldest N snapshots are actually eligible, since if we went below this we - // would fall below the configured minimum number of snapshots to keep - final boolean maybeEligible; - if (si.state() == SnapshotState.SUCCESS) { - maybeEligible = sortedSnapshots.stream() - .filter(snap -> SnapshotState.SUCCESS.equals(snap.state())) - .limit(Math.max(0, successfulSnapshotCount - minimumSnapshotCount)) - .anyMatch(si::equals); - } else if (UNSUCCESSFUL_STATES.contains(si.state())) { - maybeEligible = sortedSnapshots.contains(si); - } else { - logger.trace("[{}] INELIGIBLE because snapshot is in state [{}]", snapName, si.state()); - return false; - } - if (maybeEligible == false) { - // This snapshot is *not* one of the N oldest snapshots, so even if it were - // old enough, the other snapshots would be deleted before it - logger.trace( - "[{}]: INELIGIBLE as snapshot expiration would pass the " - + "minimum number of configured snapshots ({}) to keep, regardless of age", - snapName, - this.minimumSnapshotCount - ); - return false; - } - } - final long snapshotAge = nowSupplier.getAsLong() - si.startTime(); - if (snapshotAge > this.expireAfter.getMillis()) { - logger.trace( - () -> format( - "[%s]: ELIGIBLE as snapshot age of %s is older than %s", - snapName, - new TimeValue(snapshotAge).toHumanReadableString(3), - this.expireAfter.toHumanReadableString(3) - ) - ); - return true; + // Finally, check the expiration time of the snapshot, if it is past, then it is + // eligible for deletion + if (this.expireAfter != null) { + if (this.minimumSnapshotCount != null) { + // Only the oldest N snapshots are actually eligible, since if we went below this we + // would fall below the configured minimum number of snapshots to keep + final boolean maybeEligible; + if (si.state() == SnapshotState.SUCCESS) { + maybeEligible = sortedSnapshots.stream() + .filter(snap -> SnapshotState.SUCCESS.equals(snap.state())) + .limit(Math.max(0, successfulSnapshotCount - minimumSnapshotCount)) + .anyMatch(si::equals); + } else if (UNSUCCESSFUL_STATES.contains(si.state())) { + maybeEligible = sortedSnapshots.contains(si); } else { + logger.trace("[{}] INELIGIBLE because snapshot is in state [{}]", snapName, si.state()); + return false; + } + if (maybeEligible == false) { + // This snapshot is *not* one of the N oldest snapshots, so even if it were + // old enough, the other snapshots would be deleted before it logger.trace( - () -> format( - "[%s]: INELIGIBLE as snapshot age of [%sms] is newer than %s", - snapName, - new TimeValue(snapshotAge).toHumanReadableString(3), - this.expireAfter.toHumanReadableString(3) - ) + "[{}]: INELIGIBLE as snapshot expiration would pass the " + + "minimum number of configured snapshots ({}) to keep, regardless of age", + snapName, + this.minimumSnapshotCount ); return false; } } - // If nothing matched, the snapshot is not eligible for deletion - logger.trace("[{}]: INELIGIBLE as no retention predicates matched", snapName); - return false; - }; + final long snapshotAge = nowSupplier.getAsLong() - si.startTime(); + if (snapshotAge > this.expireAfter.getMillis()) { + logger.trace( + () -> format( + "[%s]: ELIGIBLE as snapshot age of %s is older than %s", + snapName, + new TimeValue(snapshotAge).toHumanReadableString(3), + this.expireAfter.toHumanReadableString(3) + ) + ); + return true; + } else { + logger.trace( + () -> format( + "[%s]: INELIGIBLE as snapshot age of [%sms] is newer than %s", + snapName, + new TimeValue(snapshotAge).toHumanReadableString(3), + this.expireAfter.toHumanReadableString(3) + ) + ); + return false; + } + } + // If nothing matched, the snapshot is not eligible for deletion + logger.trace("[{}]: INELIGIBLE as no retention predicates matched", snapName); + return false; } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfigurationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfigurationTests.java index 02034eb6bd2da..37d0a6a88ee4c 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfigurationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/slm/SnapshotRetentionConfigurationTests.java @@ -53,16 +53,16 @@ public void testExpireAfter() { null ); SnapshotInfo oldInfo = makeInfo(0); - assertThat(conf.getSnapshotDeletionPredicate(Collections.singletonList(oldInfo)).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, Collections.singletonList(oldInfo)), equalTo(true)); SnapshotInfo newInfo = makeInfo(1); - assertThat(conf.getSnapshotDeletionPredicate(Collections.singletonList(newInfo)).test(newInfo), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, Collections.singletonList(newInfo)), equalTo(false)); List infos = new ArrayList<>(); infos.add(newInfo); infos.add(oldInfo); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(true)); } public void testExpiredWithMinimum() { @@ -78,12 +78,12 @@ public void testExpiredWithMinimum() { List infos = new ArrayList<>(); infos.add(newInfo); infos.add(oldInfo); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(false)); conf = new SnapshotRetentionConfiguration(() -> TimeValue.timeValueDays(1).millis() + 1, TimeValue.timeValueDays(1), 1, null); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(true)); } public void testMaximum() { @@ -99,15 +99,15 @@ public void testMaximum() { SnapshotInfo s9 = makeInfo(9); List infos = Arrays.asList(s1, s2, s3, s4, s5, s6, s7, s8, s9); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s5), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s6), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s7), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s8), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s9), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s5, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s6, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s7, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s8, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s9, infos), equalTo(false)); } public void testMaximumWithExpireAfter() { @@ -122,9 +122,9 @@ public void testMaximumWithExpireAfter() { SnapshotInfo new1 = makeInfo(2); List infos = Arrays.asList(old1, old2, new1); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(old1), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(old2), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(new1), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(old1, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(old2, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(new1, infos), equalTo(false)); } public void testMaximumWithFailedOrPartial() { @@ -135,10 +135,10 @@ public void testMaximumWithFailedOrPartial() { SnapshotInfo s4 = makeInfo(4); List infos = Arrays.asList(s1, s2, s3, s4); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(false)); } public void testFailuresDeletedIfExpired() { @@ -157,16 +157,16 @@ private void assertUnsuccessfulDeletedIfExpired(boolean failure) { null ); SnapshotInfo oldInfo = makeFailureOrPartial(0, failure); - assertThat(conf.getSnapshotDeletionPredicate(Collections.singletonList(oldInfo)).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, Collections.singletonList(oldInfo)), equalTo(true)); SnapshotInfo newInfo = makeFailureOrPartial(1, failure); - assertThat(conf.getSnapshotDeletionPredicate(Collections.singletonList(newInfo)).test(newInfo), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, Collections.singletonList(newInfo)), equalTo(false)); List infos = new ArrayList<>(); infos.add(newInfo); infos.add(oldInfo); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(true)); } public void testFailuresDeletedIfNoExpiryAndMoreRecentSuccessExists() { @@ -185,10 +185,10 @@ private void assertUnsuccessfulDeletedIfNoExpiryAndMoreRecentSuccessExists(boole SnapshotInfo s4 = makeInfo(4); List infos = Arrays.asList(s1, s2, s3, s4); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(false)); } public void testFailuresKeptIfNoExpiryAndNoMoreRecentSuccess() { @@ -208,10 +208,10 @@ private void assertUnsuccessfulKeptIfNoExpiryAndNoMoreRecentSuccess(boolean fail SnapshotInfo s4 = makeFailureOrPartial(4, failure); List infos = Arrays.asList(s1, s2, s3, s4); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(false)); } public void testFailuresNotCountedTowardsMaximum() { @@ -231,11 +231,11 @@ private void assertUnsuccessfulNotCountedTowardsMaximum(boolean failure) { SnapshotInfo s5 = makeInfo(5); List infos = Arrays.asList(s1, s2, s3, s4, s5); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s5), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s5, infos), equalTo(false)); } public void testFailuresNotCountedTowardsMinimum() { @@ -261,14 +261,14 @@ private void assertUnsuccessfulNotCountedTowardsMinimum(boolean failure) { infos.add(newInfo); infos.add(failureInfo); infos.add(oldInfo); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(failureInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(failureInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(false)); conf = new SnapshotRetentionConfiguration(() -> TimeValue.timeValueDays(1).millis() + 2, TimeValue.timeValueDays(1), 1, null); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(newInfo), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(failureInfo), equalTo(true)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(oldInfo), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(newInfo, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(failureInfo, infos), equalTo(true)); + assertThat(conf.isSnapshotEligibleForDeletion(oldInfo, infos), equalTo(true)); } public void testMostRecentSuccessfulTimestampIsUsed() { @@ -280,10 +280,10 @@ public void testMostRecentSuccessfulTimestampIsUsed() { SnapshotInfo s4 = makeFailureOrPartial(4, failureBeforePartial == false); List infos = Arrays.asList(s1, s2, s3, s4); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s3), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s4), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s3, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s4, infos), equalTo(false)); } public void testFewerSuccessesThanMinWithPartial() { @@ -293,9 +293,9 @@ public void testFewerSuccessesThanMinWithPartial() { SnapshotInfo s2 = makeInfo(3); List infos = Arrays.asList(s1, sP, s2); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s1), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(sP), equalTo(false)); - assertThat(conf.getSnapshotDeletionPredicate(infos).test(s2), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s1, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(sP, infos), equalTo(false)); + assertThat(conf.isSnapshotEligibleForDeletion(s2, infos), equalTo(false)); } private SnapshotInfo makeInfo(long startTime) { diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java index 708621ee4a6c8..38290a0f1bf08 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotRetentionTask.java @@ -208,7 +208,8 @@ static boolean snapshotEligibleForDeletion( final String repository = policy.getRepository(); // Retrieve the predicate based on the retention policy, passing in snapshots pertaining only to *this* policy and repository - boolean eligible = retention.getSnapshotDeletionPredicate( + boolean eligible = retention.isSnapshotEligibleForDeletion( + snapshot, allSnapshots.get(repository) .stream() .filter( @@ -218,7 +219,7 @@ static boolean snapshotEligibleForDeletion( .orElse(false) ) .toList() - ).test(snapshot); + ); logger.debug( "[{}] testing snapshot [{}] deletion eligibility: {}", repository, From b3bce412308c79196f840d925bc9fdfb69e67e5c Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 29 Sep 2023 07:36:29 -0400 Subject: [PATCH 153/155] ESQL: Make sure the request breaker is empty (#100029) ESQL uses the request breaker to track memory and it's super important that it clear the breaker after every execution, including errors. --- .../esql/qa/server/single-node/build.gradle | 1 + .../esql/qa/single_node/EsqlClientYamlIT.java | 9 +++++++ .../xpack/esql/qa/rest/EsqlSpecTestCase.java | 25 +++++++++++++++++++ .../xpack/esql/qa/rest/RestEsqlTestCase.java | 7 ++++++ .../src/main/resources/keep.csv-spec | 3 ++- .../metadata-ignoreCsvTests.csv-spec | 3 ++- 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/qa/server/single-node/build.gradle b/x-pack/plugin/esql/qa/server/single-node/build.gradle index 6f913100e0fd7..d5fedad1a537b 100644 --- a/x-pack/plugin/esql/qa/server/single-node/build.gradle +++ b/x-pack/plugin/esql/qa/server/single-node/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'elasticsearch.legacy-yaml-rest-test' dependencies { javaRestTestImplementation project(xpackModule('esql:qa:testFixtures')) + yamlRestTestImplementation project(xpackModule('esql:qa:server')) } restResources { diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/EsqlClientYamlIT.java b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/EsqlClientYamlIT.java index 64aaf547e5468..38d58644926fe 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/EsqlClientYamlIT.java +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/EsqlClientYamlIT.java @@ -11,6 +11,9 @@ import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; +import org.elasticsearch.xpack.esql.qa.rest.EsqlSpecTestCase; +import org.junit.After; +import org.junit.Before; public class EsqlClientYamlIT extends ESClientYamlSuiteTestCase { @@ -22,4 +25,10 @@ public EsqlClientYamlIT(final ClientYamlTestCandidate testCandidate) { public static Iterable parameters() throws Exception { return createParameters(); } + + @Before + @After + public void assertRequestBreakerEmpty() throws Exception { + EsqlSpecTestCase.assertRequestBreakerEmpty(); + } } diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java index e9dc848024448..4fc89e11b7b6b 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java @@ -8,8 +8,10 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.http.HttpEntity; import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.Logger; import org.elasticsearch.test.rest.ESRestTestCase; @@ -17,6 +19,7 @@ import org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.RequestObjectBuilder; import org.elasticsearch.xpack.ql.CsvSpecReader.CsvTestCase; import org.elasticsearch.xpack.ql.SpecReader; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -25,6 +28,8 @@ import java.util.List; import java.util.Map; +import static org.elasticsearch.test.MapMatcher.assertMap; +import static org.elasticsearch.test.MapMatcher.matchesMap; import static org.elasticsearch.xpack.esql.CsvAssert.assertData; import static org.elasticsearch.xpack.esql.CsvAssert.assertMetadata; import static org.elasticsearch.xpack.esql.CsvTestUtils.isEnabled; @@ -123,4 +128,24 @@ private Throwable reworkException(Throwable th) { protected boolean preserveClusterUponCompletion() { return true; } + + @Before + @After + public void assertRequestBreakerEmptyAfterTests() throws Exception { + assertRequestBreakerEmpty(); + } + + public static void assertRequestBreakerEmpty() throws Exception { + assertBusy(() -> { + HttpEntity entity = adminClient().performRequest(new Request("GET", "/_nodes/stats")).getEntity(); + Map stats = XContentHelper.convertToMap(XContentType.JSON.xContent(), entity.getContent(), false); + Map nodes = (Map) stats.get("nodes"); + for (Object n : nodes.values()) { + Map node = (Map) n; + Map breakers = (Map) node.get("breakers"); + Map request = (Map) breakers.get("request"); + assertMap(request, matchesMap().extraOk().entry("estimated_size_in_bytes", 0).entry("estimated_size", "0b")); + } + }); + } } diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java index 2b8bead0f86bc..14e645f37a659 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java @@ -26,6 +26,7 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentType; import org.junit.After; +import org.junit.Before; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -556,4 +557,10 @@ protected static String fromIndex() { protected boolean preserveClusterUponCompletion() { return true; } + + @Before + @After + public void assertRequestBreakerEmpty() throws Exception { + EsqlSpecTestCase.assertRequestBreakerEmpty(); + } } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/keep.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/keep.csv-spec index 3637081c3c4b6..fd87eedf57f2f 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/keep.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/keep.csv-spec @@ -205,7 +205,8 @@ emp_no:integer | languages:integer | gender:keyword | first_name:keyword | abc:i 10100 | 4 | F | Hironobu | 3 ; -projectFromWithStatsAfterLimit +# awaitsfix https://github.com/elastic/elasticsearch/issues/99826 +projectFromWithStatsAfterLimit-Ignore from employees | sort emp_no | keep gender, avg_worked_seconds, first_name, last_name | limit 10 | stats m = max(avg_worked_seconds) by gender; m:long | gender:keyword diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec index 82fd27416c526..ee2d3ab6db801 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec @@ -95,7 +95,8 @@ emp_no:integer |_version:long |_index:keyword 10002 |1 |employees ; -withMvFunction +# awaitsfix https://github.com/elastic/elasticsearch/issues/99826 +withMvFunction-Ignore from employees [metadata _version] | eval i = mv_avg(_version) + 2 | stats min = min(emp_no) by i; min:integer |i:double From 554b64e36ccc4859254fd26700dd68f532ecb61e Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 29 Sep 2023 12:37:18 +0100 Subject: [PATCH 154/155] [ML] Removing uses of Version.major from ML code (#100051) We want to avoid using the member variables of the Version class wherever possible. This PR removes accesses of Version.major. This change also revealed that we were incorrectly asserting on model snapshot versions in our upgrade tests now that they are using MlConfigVersion. --- .../MachineLearningPackageLoader.java | 9 +++++---- .../upgrades/MlJobSnapshotUpgradeIT.java | 15 +++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java index 2afeda1f13512..d13d910613d31 100644 --- a/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java +++ b/x-pack/plugin/ml-package-loader/src/main/java/org/elasticsearch/xpack/ml/packageloader/MachineLearningPackageLoader.java @@ -6,7 +6,7 @@ */ package org.elasticsearch.xpack.ml.packageloader; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.bootstrap.BootstrapCheck; @@ -44,10 +44,11 @@ public class MachineLearningPackageLoader extends Plugin implements ActionPlugin // re-using thread pool setup by the ml plugin public static final String UTILITY_THREAD_POOL_NAME = "ml_utility"; + // This link will be invalid for serverless, but serverless will never be + // air-gapped, so this message should never be needed. private static final String MODEL_REPOSITORY_DOCUMENTATION_LINK = format( - "https://www.elastic.co/guide/en/machine-learning/%d.%d/ml-nlp-elser.html#air-gapped-install", - Version.CURRENT.major, - Version.CURRENT.minor + "https://www.elastic.co/guide/en/machine-learning/%s/ml-nlp-elser.html#air-gapped-install", + Build.current().version().replaceFirst("^(\\d+\\.\\d+).*", "$1") ); public MachineLearningPackageLoader() {} diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java index fa36d2aac7f14..8111e9e68df8a 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.core.TimeValue; import org.elasticsearch.xcontent.json.JsonXContent; +import org.elasticsearch.xpack.core.ml.MlConfigVersion; import org.elasticsearch.xpack.test.rest.XPackRestTestConstants; import org.junit.BeforeClass; @@ -123,8 +124,11 @@ private void testSnapshotUpgrade() throws Exception { Response getSnapshotsResponse = getModelSnapshots(JOB_ID); List> snapshots = (List>) entityAsMap(getSnapshotsResponse).get("model_snapshots"); assertThat(snapshots, hasSize(2)); - assertThat(Integer.parseInt(snapshots.get(0).get("min_version").toString(), 0, 1, 10), equalTo((int) UPGRADE_FROM_VERSION.major)); - assertThat(Integer.parseInt(snapshots.get(1).get("min_version").toString(), 0, 1, 10), equalTo((int) UPGRADE_FROM_VERSION.major)); + MlConfigVersion snapshotConfigVersion = MlConfigVersion.fromString(snapshots.get(0).get("min_version").toString()); + assertTrue( + "Expected " + snapshotConfigVersion + " not greater than " + MlConfigVersion.CURRENT, + snapshotConfigVersion.onOrBefore(MlConfigVersion.CURRENT) + ); Map snapshotToUpgrade = snapshots.stream() .filter(s -> s.get("snapshot_id").equals(currentSnapshotId) == false) @@ -232,8 +236,11 @@ private void createJobAndSnapshots() throws Exception { var modelSnapshots = entityAsMap(getModelSnapshots(JOB_ID)); var snapshots = (List>) modelSnapshots.get("model_snapshots"); assertThat(snapshots, hasSize(2)); - assertThat(Integer.parseInt(snapshots.get(0).get("min_version").toString(), 0, 1, 10), equalTo((int) UPGRADE_FROM_VERSION.major)); - assertThat(Integer.parseInt(snapshots.get(1).get("min_version").toString(), 0, 1, 10), equalTo((int) UPGRADE_FROM_VERSION.major)); + MlConfigVersion snapshotConfigVersion = MlConfigVersion.fromString(snapshots.get(0).get("min_version").toString()); + assertTrue( + "Expected " + snapshotConfigVersion + " not greater than " + MlConfigVersion.CURRENT, + snapshotConfigVersion.onOrBefore(MlConfigVersion.CURRENT) + ); } private Response buildAndPutJob(String jobId, TimeValue bucketSpan) throws Exception { From eda98a7ed70f4f4e1e81fd42f7b8c4e1bf97effe Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 29 Sep 2023 07:54:19 -0400 Subject: [PATCH 155/155] ESQL: AwaitsFix some tests that are failing These tests started failing after a combination of #100029 and #100016 which passed CI individually but aren't happy together. Proper fix incoming, but for now let's unblock some folks. --- .../qa/testFixtures/src/main/resources/math.csv-spec | 3 ++- .../src/main/resources/metadata-ignoreCsvTests.csv-spec | 3 ++- .../src/main/resources/unsigned_long.csv-spec | 6 ++++-- .../qa/testFixtures/src/main/resources/version.csv-spec | 9 ++++++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec index ebd54c4e384a7..a423db2f47df1 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec @@ -200,7 +200,8 @@ height:double | s:double 1.53 | 0.34 ; -powSalarySquared +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +powSalarySquared-Ignore from employees | eval s = pow(to_long(salary) - 75000, 2) + 10000 | keep salary, s | sort salary desc | limit 4; salary:integer | s:long diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec index ee2d3ab6db801..a38f442f6c1d9 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec @@ -112,7 +112,8 @@ emp_no:integer |_index:integer |_version:keyword 10003 |3 |version ; -multipleIndices +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +multipleIndices-Ignore // tag::multipleIndices[] FROM ul_logs, apps [METADATA _index, _version] | WHERE id IN (13, 14) AND _version == 1 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec index 9f5d7be3e63e0..bccb5a7083dac 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec @@ -138,7 +138,8 @@ FROM ul_logs 2017-11-10T20:34:43.000Z | 17764691215469285192 | 1.75E19 ; -toDegrees +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +toDegrees-Ignore FROM ul_logs | WHERE bytes_in == bytes_out | EVAL deg = TO_DEGREES(bytes_in) | KEEP bytes_in, deg ; @@ -147,7 +148,8 @@ FROM ul_logs | WHERE bytes_in == bytes_out | EVAL deg = TO_DEGREES(bytes_in) | K ; -toRadians +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +toRadians-Ignore FROM ul_logs | WHERE bytes_in == bytes_out | EVAL rad = TO_RADIANS(bytes_in) | KEEP bytes_in, rad ; diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/version.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/version.csv-spec index df1fa6e67f279..d51cb7b5d2bd0 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/version.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/version.csv-spec @@ -139,7 +139,8 @@ id:i |name:s |version:v 9 |iiiii |bad ; -orderByVersionMultipleCasts +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +orderByVersionMultipleCasts-Ignore FROM apps | EVAL o = TO_VER(CONCAT("1.", TO_STR(version))) | SORT o, id; id:i |name:s |version:v |o:v @@ -203,7 +204,8 @@ bad 5.2.9-SNAPSHOT ; -groupByVersionCast +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +groupByVersionCast-Ignore FROM apps | EVAL g = TO_VER(CONCAT("1.", TO_STR(version))) | STATS id = MAX(id) BY g | SORT id | DROP g; id:i @@ -287,7 +289,8 @@ idx:i |version:v 14 | 5.2.9 ; -case +# AwaitsFix https://github.com/elastic/elasticsearch/issues/99826 +case-Ignore FROM apps | EVAL version_text = TO_STR(version) | WHERE version IS NULL OR version_text LIKE "1*"