14
14
import org .elasticsearch .client .Response ;
15
15
import org .elasticsearch .client .ResponseException ;
16
16
import org .elasticsearch .common .settings .Settings ;
17
+ import org .elasticsearch .common .xcontent .support .XContentMapValues ;
17
18
import org .elasticsearch .test .cluster .ElasticsearchCluster ;
18
19
import org .elasticsearch .test .cluster .local .distribution .DistributionType ;
19
20
import org .elasticsearch .test .rest .ESRestTestCase ;
20
21
import org .junit .ClassRule ;
21
22
23
+ import java .io .IOException ;
22
24
import java .util .HashSet ;
23
25
import java .util .List ;
26
+ import java .util .Locale ;
24
27
import java .util .Map ;
25
28
import java .util .Set ;
26
29
27
30
import static org .hamcrest .Matchers .containsString ;
28
31
import static org .hamcrest .Matchers .equalTo ;
32
+ import static org .hamcrest .Matchers .hasSize ;
29
33
import static org .hamcrest .Matchers .lessThanOrEqualTo ;
30
34
31
35
public class EsqlPartialResultsIT extends ESRestTestCase {
@@ -97,6 +101,7 @@ public Set<String> populateIndices() throws Exception {
97
101
return okIds ;
98
102
}
99
103
104
+ @ SuppressWarnings ("unchecked" )
100
105
public void testPartialResult () throws Exception {
101
106
Set <String > okIds = populateIndices ();
102
107
String query = """
@@ -113,11 +118,30 @@ public void testPartialResult() throws Exception {
113
118
}
114
119
Response resp = client ().performRequest (request );
115
120
Map <String , Object > results = entityAsMap (resp );
121
+ logger .info ("--> results {}" , results );
116
122
assertThat (results .get ("is_partial" ), equalTo (true ));
117
123
List <?> columns = (List <?>) results .get ("columns" );
118
124
assertThat (columns , equalTo (List .of (Map .of ("name" , "fail_me" , "type" , "long" ), Map .of ("name" , "v" , "type" , "long" ))));
119
125
List <?> values = (List <?>) results .get ("values" );
120
126
assertThat (values .size (), lessThanOrEqualTo (okIds .size ()));
127
+ Map <String , Object > localInfo = (Map <String , Object >) XContentMapValues .extractValue (
128
+ results ,
129
+ "_clusters" ,
130
+ "details" ,
131
+ "(local)"
132
+ );
133
+ assertNotNull (localInfo );
134
+ assertThat (XContentMapValues .extractValue (localInfo , "_shards" , "successful" ), equalTo (0 ));
135
+ assertThat (
136
+ XContentMapValues .extractValue (localInfo , "_shards" , "failed" ),
137
+ equalTo (XContentMapValues .extractValue (localInfo , "_shards" , "total" ))
138
+ );
139
+ List <Map <String , Object >> failures = (List <Map <String , Object >>) XContentMapValues .extractValue (localInfo , "failures" );
140
+ assertThat (failures , hasSize (1 ));
141
+ assertThat (
142
+ failures .get (0 ).get ("reason" ),
143
+ equalTo (Map .of ("type" , "illegal_state_exception" , "reason" , "Accessing failing field" ))
144
+ );
121
145
}
122
146
// allow_partial_results = false
123
147
{
@@ -133,5 +157,81 @@ public void testPartialResult() throws Exception {
133
157
assertThat (resp .getStatusLine ().getStatusCode (), equalTo (500 ));
134
158
assertThat (EntityUtils .toString (resp .getEntity ()), containsString ("Accessing failing field" ));
135
159
}
160
+
161
+ }
162
+
163
+ @ SuppressWarnings ("unchecked" )
164
+ public void testFailureFromRemote () throws Exception {
165
+ setupRemoteClusters ();
166
+ try {
167
+ Set <String > okIds = populateIndices ();
168
+ String query = """
169
+ {
170
+ "query": "FROM *:ok-index,*:failing-index | LIMIT 100 | KEEP fail_me,v"
171
+ }
172
+ """ ;
173
+ // allow_partial_results = true
174
+ Request request = new Request ("POST" , "/_query" );
175
+ request .setJsonEntity (query );
176
+ if (randomBoolean ()) {
177
+ request .addParameter ("allow_partial_results" , "true" );
178
+ }
179
+ Response resp = client ().performRequest (request );
180
+ Map <String , Object > results = entityAsMap (resp );
181
+ logger .info ("--> results {}" , results );
182
+ assertThat (results .get ("is_partial" ), equalTo (true ));
183
+ List <?> columns = (List <?>) results .get ("columns" );
184
+ assertThat (columns , equalTo (List .of (Map .of ("name" , "fail_me" , "type" , "long" ), Map .of ("name" , "v" , "type" , "long" ))));
185
+ List <?> values = (List <?>) results .get ("values" );
186
+ assertThat (values .size (), lessThanOrEqualTo (okIds .size ()));
187
+ Map <String , Object > remoteCluster = (Map <String , Object >) XContentMapValues .extractValue (
188
+ results ,
189
+ "_clusters" ,
190
+ "details" ,
191
+ "cluster_one"
192
+ );
193
+ assertNotNull (remoteCluster );
194
+ assertThat (XContentMapValues .extractValue (remoteCluster , "_shards" , "successful" ), equalTo (0 ));
195
+ assertThat (
196
+ XContentMapValues .extractValue (remoteCluster , "_shards" , "failed" ),
197
+ equalTo (XContentMapValues .extractValue (remoteCluster , "_shards" , "total" ))
198
+ );
199
+ List <Map <String , Object >> failures = (List <Map <String , Object >>) XContentMapValues .extractValue (remoteCluster , "failures" );
200
+ assertThat (failures , hasSize (1 ));
201
+ assertThat (
202
+ failures .get (0 ).get ("reason" ),
203
+ equalTo (Map .of ("type" , "illegal_state_exception" , "reason" , "Accessing failing field" ))
204
+ );
205
+ } finally {
206
+ removeRemoteCluster ();
207
+ }
208
+ }
209
+
210
+ private void setupRemoteClusters () throws IOException {
211
+ String settings = String .format (Locale .ROOT , """
212
+ {
213
+ "persistent": {
214
+ "cluster": {
215
+ "remote": {
216
+ "cluster_one": {
217
+ "seeds": [ "%s" ],
218
+ "skip_unavailable": false
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
224
+ """ , cluster .getTransportEndpoints ());
225
+ Request request = new Request ("PUT" , "/_cluster/settings" );
226
+ request .setJsonEntity (settings );
227
+ client ().performRequest (request );
228
+ }
229
+
230
+ private void removeRemoteCluster () throws IOException {
231
+ Request settingsRequest = new Request ("PUT" , "/_cluster/settings" );
232
+ settingsRequest .setJsonEntity ("""
233
+ {"persistent": { "cluster.*": null}}
234
+ """ );
235
+ client ().performRequest (settingsRequest );
136
236
}
137
237
}
0 commit comments