10
10
11
11
import org .apache .lucene .search .MatchAllDocsQuery ;
12
12
import org .apache .lucene .search .Query ;
13
+ import org .elasticsearch .ElasticsearchException ;
13
14
import org .elasticsearch .action .fieldcaps .FieldCapabilities ;
15
+ import org .elasticsearch .action .fieldcaps .FieldCapabilitiesAction ;
16
+ import org .elasticsearch .action .fieldcaps .FieldCapabilitiesFailure ;
17
+ import org .elasticsearch .action .fieldcaps .FieldCapabilitiesRequest ;
14
18
import org .elasticsearch .action .fieldcaps .FieldCapabilitiesResponse ;
19
+ import org .elasticsearch .action .fieldcaps .TransportFieldCapabilitiesAction ;
15
20
import org .elasticsearch .action .index .IndexRequestBuilder ;
21
+ import org .elasticsearch .action .support .ActiveShardCount ;
22
+ import org .elasticsearch .cluster .metadata .IndexMetadata ;
23
+ import org .elasticsearch .cluster .node .DiscoveryNode ;
24
+ import org .elasticsearch .cluster .routing .allocation .command .MoveAllocationCommand ;
25
+ import org .elasticsearch .common .breaker .CircuitBreaker ;
26
+ import org .elasticsearch .common .breaker .CircuitBreakingException ;
16
27
import org .elasticsearch .common .io .stream .StreamInput ;
17
28
import org .elasticsearch .common .io .stream .StreamOutput ;
18
- import org .elasticsearch .index .mapper .DocumentParserContext ;
29
+ import org .elasticsearch .common .settings .Settings ;
30
+ import org .elasticsearch .index .IndexService ;
31
+ import org .elasticsearch .index .mapper .TimeSeriesParams ;
32
+ import org .elasticsearch .index .shard .IndexShard ;
33
+ import org .elasticsearch .index .shard .ShardId ;
34
+ import org .elasticsearch .indices .IndicesService ;
35
+ import org .elasticsearch .test .transport .MockTransportService ;
36
+ import org .elasticsearch .transport .TransportService ;
37
+ import org .elasticsearch .xcontent .XContentBuilder ;
38
+ import org .elasticsearch .xcontent .XContentFactory ;
19
39
import org .elasticsearch .index .mapper .KeywordFieldMapper ;
20
40
import org .elasticsearch .index .mapper .MetadataFieldMapper ;
21
- import org .elasticsearch .index .mapper .TimeSeriesParams ;
41
+ import org .elasticsearch .index .mapper .DocumentParserContext ;
22
42
import org .elasticsearch .index .query .AbstractQueryBuilder ;
23
43
import org .elasticsearch .index .query .QueryBuilder ;
24
44
import org .elasticsearch .index .query .QueryBuilders ;
28
48
import org .elasticsearch .plugins .Plugin ;
29
49
import org .elasticsearch .plugins .SearchPlugin ;
30
50
import org .elasticsearch .test .ESIntegTestCase ;
31
- import org .elasticsearch .transport .RemoteTransportException ;
32
- import org .elasticsearch .xcontent .XContentBuilder ;
33
- import org .elasticsearch .xcontent .XContentFactory ;
34
51
import org .junit .Before ;
35
52
36
53
import java .io .IOException ;
41
58
import java .util .HashMap ;
42
59
import java .util .List ;
43
60
import java .util .Map ;
61
+ import java .util .concurrent .atomic .AtomicBoolean ;
44
62
import java .util .function .Function ;
45
63
import java .util .function .Predicate ;
46
64
47
65
import static java .util .Collections .singletonList ;
48
66
import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertAcked ;
67
+ import static org .hamcrest .Matchers .aMapWithSize ;
49
68
import static org .hamcrest .Matchers .array ;
50
69
import static org .hamcrest .Matchers .arrayContainingInAnyOrder ;
51
70
import static org .hamcrest .Matchers .containsInAnyOrder ;
52
71
import static org .hamcrest .Matchers .equalTo ;
72
+ import static org .hamcrest .Matchers .hasKey ;
73
+ import static org .hamcrest .Matchers .hasSize ;
53
74
54
75
public class FieldCapabilitiesIT extends ESIntegTestCase {
55
76
77
+ @ Override
78
+ protected Collection <Class <? extends Plugin >> getMockPlugins () {
79
+ final Collection <Class <? extends Plugin >> plugins = new ArrayList <>(super .getMockPlugins ());
80
+ plugins .add (MockTransportService .TestPlugin .class );
81
+ return plugins ;
82
+ }
83
+
56
84
@ Override
57
85
@ Before
58
86
public void setUp () throws Exception {
@@ -335,9 +363,8 @@ public void testFailures() throws InterruptedException {
335
363
assertEquals (2 , response .getFailedIndices ().length );
336
364
assertThat (response .getFailures ().get (0 ).getIndices (), arrayContainingInAnyOrder ("index1-error" , "index2-error" ));
337
365
Exception failure = response .getFailures ().get (0 ).getException ();
338
- assertEquals (RemoteTransportException .class , failure .getClass ());
339
- assertEquals (IllegalArgumentException .class , failure .getCause ().getClass ());
340
- assertEquals ("I throw because I choose to." , failure .getCause ().getMessage ());
366
+ assertEquals (IllegalArgumentException .class , failure .getClass ());
367
+ assertEquals ("I throw because I choose to." , failure .getMessage ());
341
368
342
369
// the "indices" section should not include failed ones
343
370
assertThat (Arrays .asList (response .getIndices ()), containsInAnyOrder ("old_index" , "new_index" ));
@@ -352,6 +379,163 @@ public void testFailures() throws InterruptedException {
352
379
assertEquals ("I throw because I choose to." , ex .getMessage ());
353
380
}
354
381
382
+ private void populateTimeRangeIndices () throws Exception {
383
+ internalCluster ().ensureAtLeastNumDataNodes (2 );
384
+ assertAcked (prepareCreate ("log-index-1" )
385
+ .setSettings (Settings .builder ()
386
+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , between (1 , 5 ))
387
+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 1 ))
388
+ .setMapping ("timestamp" , "type=date" , "field1" , "type=keyword" ));
389
+ assertAcked (prepareCreate ("log-index-2" )
390
+ .setSettings (Settings .builder ()
391
+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , between (1 , 5 ))
392
+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 1 ))
393
+ .setMapping ("timestamp" , "type=date" , "field1" , "type=long" ));
394
+ List <IndexRequestBuilder > reqs = new ArrayList <>();
395
+ reqs .add (client ().prepareIndex ("log-index-1" ).setSource ("timestamp" , "2015-07-08" ));
396
+ reqs .add (client ().prepareIndex ("log-index-1" ).setSource ("timestamp" , "2018-07-08" ));
397
+ reqs .add (client ().prepareIndex ("log-index-1" ).setSource ("timestamp" , "2020-03-03" ));
398
+ reqs .add (client ().prepareIndex ("log-index-1" ).setSource ("timestamp" , "2020-09-09" ));
399
+ reqs .add (client ().prepareIndex ("log-index-2" ).setSource ("timestamp" , "2019-10-12" ));
400
+ reqs .add (client ().prepareIndex ("log-index-2" ).setSource ("timestamp" , "2020-02-02" ));
401
+ reqs .add (client ().prepareIndex ("log-index-2" ).setSource ("timestamp" , "2020-10-10" ));
402
+ indexRandom (true , reqs );
403
+ ensureGreen ("log-index-1" , "log-index-2" );
404
+ client ().admin ().indices ().prepareRefresh ("log-index-1" , "log-index-2" ).get ();
405
+ }
406
+
407
+ public void testTargetNodeFails () throws Exception {
408
+ populateTimeRangeIndices ();
409
+ try {
410
+ final AtomicBoolean failedRequest = new AtomicBoolean ();
411
+ for (String node : internalCluster ().getNodeNames ()) {
412
+ MockTransportService transportService = (MockTransportService ) internalCluster ().getInstance (TransportService .class , node );
413
+ transportService .addRequestHandlingBehavior (TransportFieldCapabilitiesAction .ACTION_NODE_NAME ,
414
+ (handler , request , channel , task ) -> {
415
+ if (failedRequest .compareAndSet (false , true )) {
416
+ channel .sendResponse (new CircuitBreakingException ("Simulated" , CircuitBreaker .Durability .TRANSIENT ));
417
+ } else {
418
+ handler .messageReceived (request , channel , task );
419
+ }
420
+ });
421
+ }
422
+ FieldCapabilitiesRequest request = new FieldCapabilitiesRequest ();
423
+ request .indices ("log-index-*" );
424
+ request .fields ("*" );
425
+ if (randomBoolean ()) {
426
+ request .indexFilter (QueryBuilders .rangeQuery ("timestamp" ).gte ("2020-01-01" ));
427
+ }
428
+ final FieldCapabilitiesResponse response = client ().execute (FieldCapabilitiesAction .INSTANCE , request ).actionGet ();
429
+ assertTrue (failedRequest .get ());
430
+ assertThat (response .getIndices (), arrayContainingInAnyOrder ("log-index-1" , "log-index-2" ));
431
+ assertThat (response .getField ("field1" ), aMapWithSize (2 ));
432
+ assertThat (response .getField ("field1" ), hasKey ("long" ));
433
+ assertThat (response .getField ("field1" ), hasKey ("keyword" ));
434
+ } finally {
435
+ for (String node : internalCluster ().getNodeNames ()) {
436
+ MockTransportService transportService = (MockTransportService ) internalCluster ().getInstance (TransportService .class , node );
437
+ transportService .clearAllRules ();
438
+ }
439
+ }
440
+ }
441
+
442
+ public void testNoActiveCopy () throws Exception {
443
+ assertAcked (prepareCreate ("log-index-inactive" )
444
+ .setSettings (Settings .builder ()
445
+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , between (1 , 5 ))
446
+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 1 )
447
+ .put ("index.routing.allocation.require._id" , "unknown" ))
448
+ .setWaitForActiveShards (ActiveShardCount .NONE )
449
+ .setMapping ("timestamp" , "type=date" , "field1" , "type=keyword" ));
450
+ {
451
+ final ElasticsearchException ex =
452
+ expectThrows (ElasticsearchException .class , () -> client ().prepareFieldCaps ("log-index-*" ).setFields ("*" ).get ());
453
+ assertThat (ex .getMessage (), equalTo ("index [log-index-inactive] has no active shard copy" ));
454
+ }
455
+ {
456
+ populateTimeRangeIndices ();
457
+ FieldCapabilitiesRequest request = new FieldCapabilitiesRequest ();
458
+ request .indices ("log-index-*" );
459
+ request .fields ("*" );
460
+ if (randomBoolean ()) {
461
+ request .indexFilter (QueryBuilders .rangeQuery ("timestamp" ).gte ("2020-01-01" ));
462
+ }
463
+ final FieldCapabilitiesResponse response = client ().execute (FieldCapabilitiesAction .INSTANCE , request ).actionGet ();
464
+ assertThat (response .getIndices (), arrayContainingInAnyOrder ("log-index-1" , "log-index-2" ));
465
+ assertThat (response .getField ("field1" ), aMapWithSize (2 ));
466
+ assertThat (response .getField ("field1" ), hasKey ("long" ));
467
+ assertThat (response .getField ("field1" ), hasKey ("long" ));
468
+
469
+ assertThat (response .getFailures (), hasSize (1 ));
470
+ final FieldCapabilitiesFailure failure = response .getFailures ().get (0 );
471
+ assertThat (failure .getIndices (), arrayContainingInAnyOrder ("log-index-inactive" ));
472
+ assertThat (failure .getException ().getMessage (), equalTo ("index [log-index-inactive] has no active shard copy" ));
473
+ }
474
+ }
475
+
476
+ private void moveOrCloseShardsOnNodes (String nodeName ) throws Exception {
477
+ final IndicesService indicesService = internalCluster ().getInstance (IndicesService .class , nodeName );
478
+ for (IndexService indexService : indicesService ) {
479
+ for (IndexShard indexShard : indexService ) {
480
+ if (randomBoolean ()) {
481
+ indexShard .close ("test" , randomBoolean ());
482
+ } else if (randomBoolean ()) {
483
+ final ShardId shardId = indexShard .shardId ();
484
+ final String [] nodeNames = internalCluster ().getNodeNames ();
485
+ final String newNodeName = randomValueOtherThanMany (n -> nodeName .equals (n ) == false , () -> randomFrom (nodeNames ));
486
+ DiscoveryNode fromNode = null ;
487
+ DiscoveryNode toNode = null ;
488
+ for (DiscoveryNode node : clusterService ().state ().nodes ()) {
489
+ if (node .getName ().equals (nodeName )) {
490
+ fromNode = node ;
491
+ }
492
+ if (node .getName ().equals (newNodeName )) {
493
+ toNode = node ;
494
+ }
495
+ }
496
+ assertNotNull (fromNode );
497
+ assertNotNull (toNode );
498
+ client ().admin ().cluster ().prepareReroute ()
499
+ .add (new MoveAllocationCommand (shardId .getIndexName (), shardId .id (), fromNode .getId (), toNode .getId ()))
500
+ .execute ().actionGet ();
501
+ }
502
+ }
503
+ }
504
+ }
505
+
506
+ public void testRelocation () throws Exception {
507
+ populateTimeRangeIndices ();
508
+ try {
509
+ final AtomicBoolean relocated = new AtomicBoolean ();
510
+ for (String node : internalCluster ().getNodeNames ()) {
511
+ MockTransportService transportService = (MockTransportService ) internalCluster ().getInstance (TransportService .class , node );
512
+ transportService .addRequestHandlingBehavior (TransportFieldCapabilitiesAction .ACTION_NODE_NAME ,
513
+ (handler , request , channel , task ) -> {
514
+ if (relocated .compareAndSet (false , true )) {
515
+ moveOrCloseShardsOnNodes (node );
516
+ }
517
+ handler .messageReceived (request , channel , task );
518
+ });
519
+ }
520
+ FieldCapabilitiesRequest request = new FieldCapabilitiesRequest ();
521
+ request .indices ("log-index-*" );
522
+ request .fields ("*" );
523
+ if (randomBoolean ()) {
524
+ request .indexFilter (QueryBuilders .rangeQuery ("timestamp" ).gte ("2020-01-01" ));
525
+ }
526
+ final FieldCapabilitiesResponse response = client ().execute (FieldCapabilitiesAction .INSTANCE , request ).actionGet ();
527
+ assertThat (response .getIndices (), arrayContainingInAnyOrder ("log-index-1" , "log-index-2" ));
528
+ assertThat (response .getField ("field1" ), aMapWithSize (2 ));
529
+ assertThat (response .getField ("field1" ), hasKey ("long" ));
530
+ assertThat (response .getField ("field1" ), hasKey ("long" ));
531
+ } finally {
532
+ for (String node : internalCluster ().getNodeNames ()) {
533
+ MockTransportService transportService = (MockTransportService ) internalCluster ().getInstance (TransportService .class , node );
534
+ transportService .clearAllRules ();
535
+ }
536
+ }
537
+ }
538
+
355
539
private void assertIndices (FieldCapabilitiesResponse response , String ... indices ) {
356
540
assertNotNull (response .getIndices ());
357
541
Arrays .sort (indices );
0 commit comments