Skip to content

Commit 43398ca

Browse files
Merge branch 'main' into Add-Hugging-Face-Rerank-support
2 parents 68755a6 + 2bf6d54 commit 43398ca

File tree

17 files changed

+332
-44
lines changed

17 files changed

+332
-44
lines changed

plugins/examples/custom-suggester/src/main/java/org/elasticsearch/example/customsuggester/CustomSuggester.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import org.apache.lucene.search.IndexSearcher;
1313
import org.apache.lucene.util.CharsRefBuilder;
14-
import org.elasticsearch.common.text.Text;
14+
import org.elasticsearch.xcontent.Text;
1515
import org.elasticsearch.search.suggest.Suggest;
1616
import org.elasticsearch.search.suggest.Suggester;
1717

plugins/examples/custom-suggester/src/main/java/org/elasticsearch/example/customsuggester/CustomSuggestion.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import org.elasticsearch.common.io.stream.StreamInput;
1313
import org.elasticsearch.common.io.stream.StreamOutput;
14-
import org.elasticsearch.common.text.Text;
14+
import org.elasticsearch.xcontent.Text;
1515
import org.elasticsearch.search.suggest.Suggest;
1616
import org.elasticsearch.xcontent.ParseField;
1717
import org.elasticsearch.xcontent.XContentBuilder;

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexStateService.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import org.elasticsearch.core.Tuple;
6666
import org.elasticsearch.index.Index;
6767
import org.elasticsearch.index.IndexNotFoundException;
68+
import org.elasticsearch.index.IndexReshardService;
6869
import org.elasticsearch.index.IndexVersion;
6970
import org.elasticsearch.index.shard.IndexLongFieldRange;
7071
import org.elasticsearch.index.shard.ShardId;
@@ -352,6 +353,12 @@ static ClusterState addIndexClosedBlocks(
352353
);
353354
}
354355

356+
// Check if index closing conflicts with ongoing resharding
357+
Set<Index> reshardingIndices = IndexReshardService.reshardingIndices(currentProjectState, indicesToClose);
358+
if (reshardingIndices.isEmpty() == false) {
359+
throw new IllegalArgumentException("Cannot close indices that are being resharded: " + reshardingIndices);
360+
}
361+
355362
final ClusterBlocks.Builder blocks = ClusterBlocks.builder(currentState.blocks());
356363

357364
for (Index index : indicesToClose) {
@@ -940,6 +947,22 @@ static Tuple<ClusterState, List<IndexResult>> closeRoutingTable(
940947
continue;
941948
}
942949

950+
// Check if index closing conflicts with ongoing resharding
951+
Set<Index> reshardingIndices = IndexReshardService.reshardingIndices(currentProjectState, Set.of(index));
952+
if (reshardingIndices.isEmpty() == false) {
953+
closingResults.put(
954+
result.getKey(),
955+
new IndexResult(
956+
result.getKey(),
957+
new IllegalStateException(
958+
"verification of shards before closing " + index + " succeeded but index is being resharded in the meantime"
959+
)
960+
)
961+
);
962+
logger.debug("verification of shards before closing {} succeeded but index is being resharded in the meantime", index);
963+
continue;
964+
}
965+
943966
blocks.removeIndexBlockWithId(projectId, index.getName(), INDEX_CLOSED_BLOCK_ID);
944967
blocks.addIndexBlock(projectId, index.getName(), INDEX_CLOSED_BLOCK);
945968
final IndexMetadata.Builder updatedMetadata = IndexMetadata.builder(indexMetadata).state(IndexMetadata.State.CLOSE);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index;
11+
12+
import org.elasticsearch.cluster.ProjectState;
13+
import org.elasticsearch.cluster.metadata.IndexMetadata;
14+
15+
import java.util.HashSet;
16+
import java.util.Set;
17+
18+
/**
19+
* Resharding is currently only implemented in Serverless. This service only exposes minimal metadata about resharding
20+
* needed by other services.
21+
*/
22+
public class IndexReshardService {
23+
/**
24+
* Returns the indices from the provided set that are currently being resharded.
25+
*/
26+
public static Set<Index> reshardingIndices(final ProjectState projectState, final Set<Index> indicesToCheck) {
27+
final Set<Index> indices = new HashSet<>();
28+
for (Index index : indicesToCheck) {
29+
IndexMetadata indexMetadata = projectState.metadata().index(index);
30+
31+
if (indexMetadata != null && indexMetadata.getReshardingMetadata() != null) {
32+
indices.add(index);
33+
}
34+
}
35+
return indices;
36+
}
37+
}

server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexStateServiceTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.cluster.routing.UnassignedInfo;
2828
import org.elasticsearch.common.settings.Settings;
2929
import org.elasticsearch.core.Nullable;
30+
import org.elasticsearch.core.Tuple;
3031
import org.elasticsearch.index.Index;
3132
import org.elasticsearch.index.IndexNotFoundException;
3233
import org.elasticsearch.index.IndexVersion;
@@ -45,6 +46,7 @@
4546
import java.util.Collections;
4647
import java.util.HashMap;
4748
import java.util.HashSet;
49+
import java.util.List;
4850
import java.util.Map;
4951
import java.util.Set;
5052
import java.util.stream.Collectors;
@@ -164,6 +166,38 @@ public void testCloseRoutingTableWithSnapshottedIndex() {
164166
assertThat(updatedState.blocks().hasIndexBlockWithId(projectId, index.getName(), INDEX_CLOSED_BLOCK_ID), is(true));
165167
}
166168

169+
public void testCloseRoutingTableWithReshardingIndex() {
170+
ClusterState state = stateWithProject("testCloseRoutingTableWithReshardingIndex", projectId);
171+
172+
String indexName = "resharding-index";
173+
ClusterBlock block = MetadataIndexStateService.createIndexClosingBlock();
174+
state = addOpenedIndex(projectId, indexName, randomIntBetween(1, 5), randomIntBetween(0, 5), state);
175+
176+
var updatedMetadata = IndexMetadata.builder(state.metadata().getProject(projectId).index(indexName))
177+
.reshardingMetadata(IndexReshardingMetadata.newSplitByMultiple(randomIntBetween(1, 5), 2))
178+
.build();
179+
state = ClusterState.builder(state)
180+
.putProjectMetadata(ProjectMetadata.builder(state.metadata().getProject(projectId)).put(updatedMetadata, true))
181+
.build();
182+
183+
state = ClusterState.builder(state)
184+
.blocks(ClusterBlocks.builder().blocks(state.blocks()).addIndexBlock(projectId, indexName, block))
185+
.build();
186+
187+
final Index index = state.metadata().getProject(projectId).index(indexName).getIndex();
188+
final Tuple<ClusterState, List<IndexResult>> result = MetadataIndexStateService.closeRoutingTable(
189+
state,
190+
projectId,
191+
Map.of(index, block),
192+
Map.of(index, new IndexResult(index)),
193+
TestShardRoutingRoleStrategies.DEFAULT_ROLE_ONLY
194+
);
195+
final ClusterState updatedState = result.v1();
196+
assertIsOpened(index.getName(), updatedState, projectId);
197+
assertThat(updatedState.blocks().hasIndexBlockWithId(projectId, index.getName(), INDEX_CLOSED_BLOCK_ID), is(true));
198+
assertThat(result.v2().get(0).getException(), notNullValue());
199+
}
200+
167201
public void testAddIndexClosedBlocks() {
168202
final ClusterState initialState = stateWithProject("testAddIndexClosedBlocks", projectId);
169203
{
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index;
11+
12+
import org.elasticsearch.cluster.ClusterName;
13+
import org.elasticsearch.cluster.ClusterState;
14+
import org.elasticsearch.cluster.metadata.IndexMetadata;
15+
import org.elasticsearch.cluster.metadata.IndexReshardingMetadata;
16+
import org.elasticsearch.cluster.metadata.ProjectMetadata;
17+
import org.elasticsearch.test.ESTestCase;
18+
19+
import java.util.HashSet;
20+
import java.util.Set;
21+
22+
public class IndexReshardServiceTests extends ESTestCase {
23+
public void testReshardingIndices() {
24+
var indexMetadata1 = IndexMetadata.builder(randomAlphaOfLength(5)).settings(indexSettings(IndexVersion.current(), 1, 0)).build();
25+
var indexMetadata2 = IndexMetadata.builder(indexMetadata1.getIndex().getName() + "2")
26+
.settings(indexSettings(IndexVersion.current(), 1, 0))
27+
.reshardingMetadata(IndexReshardingMetadata.newSplitByMultiple(1, 2))
28+
.build();
29+
30+
var projectId = randomProjectIdOrDefault();
31+
32+
var projectMetadataBuilder = ProjectMetadata.builder(projectId);
33+
projectMetadataBuilder.put(indexMetadata1, false);
34+
projectMetadataBuilder.put(indexMetadata2, false);
35+
36+
final ClusterState clusterState = ClusterState.builder(new ClusterName("testReshardingIndices"))
37+
.putProjectMetadata(projectMetadataBuilder)
38+
.build();
39+
40+
Set<Index> indicesToCheck = new HashSet<>();
41+
indicesToCheck.add(indexMetadata1.getIndex());
42+
indicesToCheck.add(indexMetadata2.getIndex());
43+
44+
Set<Index> reshardingIndices = IndexReshardService.reshardingIndices(clusterState.projectState(projectId), indicesToCheck);
45+
46+
assertEquals(1, reshardingIndices.size());
47+
assertTrue(reshardingIndices.contains(indexMetadata2.getIndex()));
48+
}
49+
}

x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import static org.elasticsearch.xpack.esql.CsvTestUtils.isEnabled;
4747
import static org.elasticsearch.xpack.esql.CsvTestsDataLoader.ENRICH_SOURCE_INDICES;
4848
import static org.elasticsearch.xpack.esql.EsqlTestUtils.classpathResources;
49-
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.FORK_V3;
49+
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.FORK_V4;
5050
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINESTATS;
5151
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINESTATS_V2;
5252
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINESTATS_V7;
@@ -132,7 +132,7 @@ protected void shouldSkipTest(String testName) throws IOException {
132132
assumeFalse("LOOKUP JOIN not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_LOOKUP_V12.capabilityName()));
133133
// Unmapped fields require a coorect capability response from every cluster, which isn't currently implemented.
134134
assumeFalse("UNMAPPED FIELDS not yet supported in CCS", testCase.requiredCapabilities.contains(UNMAPPED_FIELDS.capabilityName()));
135-
assumeFalse("FORK not yet supported in CCS", testCase.requiredCapabilities.contains(FORK_V3.capabilityName()));
135+
assumeFalse("FORK not yet supported in CCS", testCase.requiredCapabilities.contains(FORK_V4.capabilityName()));
136136
}
137137

138138
@Override

x-pack/plugin/esql/qa/testFixtures/src/main/resources/fork.csv-spec

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44

55
simpleFork
6-
required_capability: fork_v3
6+
required_capability: fork_v4
77

88
FROM employees
99
| FORK ( WHERE emp_no == 10001 )
@@ -18,7 +18,7 @@ emp_no:integer | _fork:keyword
1818
;
1919

2020
forkWithWhereSortAndLimit
21-
required_capability: fork_v3
21+
required_capability: fork_v4
2222

2323
FROM employees
2424
| FORK ( WHERE hire_date < "1985-03-01T00:00:00Z" | SORT first_name | LIMIT 5 )
@@ -38,7 +38,7 @@ emp_no:integer | first_name:keyword | _fork:keyword
3838
;
3939

4040
fiveFork
41-
required_capability: fork_v3
41+
required_capability: fork_v4
4242

4343
FROM employees
4444
| FORK ( WHERE emp_no == 10005 )
@@ -59,7 +59,7 @@ fork5 | 10001
5959
;
6060

6161
forkWithWhereSortDescAndLimit
62-
required_capability: fork_v3
62+
required_capability: fork_v4
6363

6464
FROM employees
6565
| FORK ( WHERE hire_date < "1985-03-01T00:00:00Z" | SORT first_name DESC | LIMIT 2 )
@@ -76,7 +76,7 @@ fork2 | 10087 | Xinglin
7676
;
7777

7878
forkWithCommonPrefilter
79-
required_capability: fork_v3
79+
required_capability: fork_v4
8080

8181
FROM employees
8282
| WHERE emp_no > 10050
@@ -94,7 +94,7 @@ fork2 | 10100
9494
;
9595

9696
forkWithSemanticSearchAndScore
97-
required_capability: fork_v3
97+
required_capability: fork_v4
9898
required_capability: semantic_text_field_caps
9999
required_capability: metadata_score
100100

@@ -114,7 +114,7 @@ fork2 | 6.093784261960139E18 | 2 | all we have to decide is w
114114
;
115115

116116
forkWithEvals
117-
required_capability: fork_v3
117+
required_capability: fork_v4
118118

119119
FROM employees
120120
| FORK (WHERE emp_no == 10048 OR emp_no == 10081 | EVAL x = "abc" | EVAL y = 1)
@@ -131,7 +131,7 @@ fork2 | 10087 | def | null | 2
131131
;
132132

133133
forkWithStats
134-
required_capability: fork_v3
134+
required_capability: fork_v4
135135

136136
FROM employees
137137
| FORK (WHERE emp_no == 10048 OR emp_no == 10081)
@@ -152,7 +152,7 @@ fork4 | null | 100 | 10001 | null
152152
;
153153

154154
forkWithDissect
155-
required_capability: fork_v3
155+
required_capability: fork_v4
156156

157157
FROM employees
158158
| WHERE emp_no == 10048 OR emp_no == 10081
@@ -172,7 +172,7 @@ fork2 | 10081 | Rosen | 10081 | null | Zhongwei
172172
;
173173

174174
forkWithMixOfCommands
175-
required_capability: fork_v3
175+
required_capability: fork_v4
176176

177177
FROM employees
178178
| WHERE emp_no == 10048 OR emp_no == 10081
@@ -195,3 +195,24 @@ fork3 | 10081 | Rosen | null | null | null
195195
fork4 | 10048 | abc | aaa | null | null
196196
fork4 | 10081 | abc | aaa | null | null
197197
;
198+
199+
forkWithFiltersOnConstantValues
200+
required_capability: fork_v4
201+
202+
FROM employees
203+
| EVAL z = 1
204+
| WHERE z == 1
205+
| FORK (WHERE emp_no == 10048 OR emp_no == 10081 | WHERE z - 1 == 0)
206+
(WHERE emp_no == 10081 OR emp_no == 10087 | EVAL a = "x" )
207+
(STATS x = COUNT(*), y = MAX(emp_no), z = MIN(emp_no) | EVAL a = "y" )
208+
(STATS x = COUNT(*), y = MIN(emp_no))
209+
| WHERE _fork == "fork2" OR a == "y"
210+
| KEEP _fork, emp_no, x, y, z
211+
| SORT _fork, emp_no
212+
;
213+
214+
_fork:keyword | emp_no:integer | x:long | y:integer | z:integer
215+
fork2 | 10081 | null | null | 1
216+
fork2 | 10087 | null | null | 1
217+
fork3 | null | 100 | 10100 | 10001
218+
;

x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/ForkIT.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,51 @@ public void testWithStatsSimple() {
585585
}
586586
}
587587

588+
public void testWithConditionOnForkField() {
589+
var query = """
590+
FROM test
591+
| FORK ( WHERE content:"fox" | EVAL a = 1)
592+
( WHERE content:"cat" | EVAL b = 2 )
593+
( WHERE content:"dog" | EVAL c = 3 )
594+
| WHERE _fork == "fork2"
595+
| KEEP _fork, id, content, a, b, c
596+
| SORT _fork
597+
""";
598+
599+
try (var resp = run(query)) {
600+
assertColumnNames(resp.columns(), List.of("_fork", "id", "content", "a", "b", "c"));
601+
602+
Iterable<Iterable<Object>> expectedValues = List.of(
603+
Arrays.stream(new Object[] { "fork2", 5, "There is also a white cat", null, 2, null }).toList()
604+
);
605+
assertValues(resp.values(), expectedValues);
606+
}
607+
}
608+
609+
public void testWithFilteringOnConstantColumn() {
610+
var query = """
611+
FROM test
612+
| FORK ( WHERE content:"fox" | EVAL a = 1)
613+
( WHERE content:"cat" | EVAL a = 2 )
614+
( WHERE content:"dog" | EVAL a = 3 )
615+
| WHERE a == 3
616+
| KEEP _fork, id, content, a
617+
| SORT id
618+
| LIMIT 3
619+
""";
620+
621+
try (var resp = run(query)) {
622+
assertColumnNames(resp.columns(), List.of("_fork", "id", "content", "a"));
623+
624+
Iterable<Iterable<Object>> expectedValues = List.of(
625+
List.of("fork3", 2, "This is a brown dog", 3),
626+
List.of("fork3", 3, "This dog is really brown", 3),
627+
List.of("fork3", 4, "The dog is brown but this document is very very long", 3)
628+
);
629+
assertValues(resp.values(), expectedValues);
630+
}
631+
}
632+
588633
public void testWithEvalWithConflictingTypes() {
589634
var query = """
590635
FROM test

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ public enum Cap {
10131013
/**
10141014
* Support streaming of sub plan results
10151015
*/
1016-
FORK_V3(Build.current().isSnapshot()),
1016+
FORK_V4(Build.current().isSnapshot()),
10171017

10181018
/**
10191019
* Support for the {@code leading_zeros} named parameter.

0 commit comments

Comments
 (0)