1919
2020package org .elasticsearch .action .bulk ;
2121
22+ import org .apache .logging .log4j .LogManager ;
23+ import org .apache .logging .log4j .Logger ;
2224import org .apache .logging .log4j .message .ParameterizedMessage ;
2325import org .apache .lucene .util .SparseFixedBitSet ;
2426import org .elasticsearch .ElasticsearchParseException ;
8082import java .util .Set ;
8183import java .util .concurrent .TimeUnit ;
8284import java .util .concurrent .atomic .AtomicInteger ;
85+ import java .util .concurrent .atomic .AtomicIntegerArray ;
8386import java .util .function .LongSupplier ;
8487import java .util .function .Supplier ;
8588import java .util .stream .Collectors ;
@@ -581,14 +584,13 @@ private long relativeTime() {
581584 }
582585
583586 void processBulkIndexIngestRequest (Task task , BulkRequest original , ActionListener <BulkResponse > listener ) {
584- long ingestStartTimeInNanos = System .nanoTime ();
585- BulkRequestModifier bulkRequestModifier = new BulkRequestModifier (original );
586- ingestService .executeBulkRequest (() -> bulkRequestModifier ,
587- (indexRequest , exception ) -> {
588- logger .debug (() -> new ParameterizedMessage ("failed to execute pipeline [{}] for document [{}/{}/{}]" ,
589- indexRequest .getPipeline (), indexRequest .index (), indexRequest .type (), indexRequest .id ()), exception );
590- bulkRequestModifier .markCurrentItemAsFailed (exception );
591- }, (exception ) -> {
587+ final long ingestStartTimeInNanos = System .nanoTime ();
588+ final BulkRequestModifier bulkRequestModifier = new BulkRequestModifier (original );
589+ ingestService .executeBulkRequest (
590+ original .numberOfActions (),
591+ () -> bulkRequestModifier ,
592+ bulkRequestModifier ::markItemAsFailed ,
593+ (originalThread , exception ) -> {
592594 if (exception != null ) {
593595 logger .error ("failed to execute pipeline for a bulk request" , exception );
594596 listener .onFailure (exception );
@@ -603,26 +605,56 @@ void processBulkIndexIngestRequest(Task task, BulkRequest original, ActionListen
603605 // (this will happen if pre-processing all items in the bulk failed)
604606 actionListener .onResponse (new BulkResponse (new BulkItemResponse [0 ], 0 ));
605607 } else {
606- doExecute (task , bulkRequest , actionListener );
608+ // If a processor went async and returned a response on a different thread then
609+ // before we continue the bulk request we should fork back on a write thread:
610+ if (originalThread == Thread .currentThread ()) {
611+ assert Thread .currentThread ().getName ().contains (ThreadPool .Names .WRITE );
612+ doExecute (task , bulkRequest , actionListener );
613+ } else {
614+ threadPool .executor (ThreadPool .Names .WRITE ).execute (new AbstractRunnable () {
615+ @ Override
616+ public void onFailure (Exception e ) {
617+ listener .onFailure (e );
618+ }
619+
620+ @ Override
621+ protected void doRun () throws Exception {
622+ doExecute (task , bulkRequest , actionListener );
623+ }
624+
625+ @ Override
626+ public boolean isForceExecution () {
627+ // If we fork back to a write thread we **not** should fail, because tp queue is full.
628+ // (Otherwise the work done during ingest will be lost)
629+ // It is okay to force execution here. Throttling of write requests happens prior to
630+ // ingest when a node receives a bulk request.
631+ return true ;
632+ }
633+ });
634+ }
607635 }
608636 }
609637 },
610- indexRequest -> bulkRequestModifier .markCurrentItemAsDropped ());
638+ bulkRequestModifier ::markItemAsDropped
639+ );
611640 }
612641
613642 static final class BulkRequestModifier implements Iterator <DocWriteRequest <?>> {
614643
644+ private static final Logger LOGGER = LogManager .getLogger (BulkRequestModifier .class );
645+
615646 final BulkRequest bulkRequest ;
616647 final SparseFixedBitSet failedSlots ;
617648 final List <BulkItemResponse > itemResponses ;
649+ final AtomicIntegerArray originalSlots ;
618650
619- int currentSlot = -1 ;
620- int [] originalSlots ;
651+ volatile int currentSlot = -1 ;
621652
622653 BulkRequestModifier (BulkRequest bulkRequest ) {
623654 this .bulkRequest = bulkRequest ;
624655 this .failedSlots = new SparseFixedBitSet (bulkRequest .requests ().size ());
625656 this .itemResponses = new ArrayList <>(bulkRequest .requests ().size ());
657+ this .originalSlots = new AtomicIntegerArray (bulkRequest .requests ().size ()); // oversize, but that's ok
626658 }
627659
628660 @ Override
@@ -646,12 +678,11 @@ BulkRequest getBulkRequest() {
646678
647679 int slot = 0 ;
648680 List <DocWriteRequest <?>> requests = bulkRequest .requests ();
649- originalSlots = new int [requests .size ()]; // oversize, but that's ok
650681 for (int i = 0 ; i < requests .size (); i ++) {
651682 DocWriteRequest <?> request = requests .get (i );
652683 if (failedSlots .get (i ) == false ) {
653684 modifiedBulkRequest .add (request );
654- originalSlots [ slot ++] = i ;
685+ originalSlots . set ( slot ++, i ) ;
655686 }
656687 }
657688 return modifiedBulkRequest ;
@@ -666,7 +697,7 @@ ActionListener<BulkResponse> wrapActionListenerIfNeeded(long ingestTookInMillis,
666697 return ActionListener .delegateFailure (actionListener , (delegatedListener , response ) -> {
667698 BulkItemResponse [] items = response .getItems ();
668699 for (int i = 0 ; i < items .length ; i ++) {
669- itemResponses .add (originalSlots [ i ] , response .getItems ()[i ]);
700+ itemResponses .add (originalSlots . get ( i ) , response .getItems ()[i ]);
670701 }
671702 delegatedListener .onResponse (
672703 new BulkResponse (
@@ -675,11 +706,11 @@ ActionListener<BulkResponse> wrapActionListenerIfNeeded(long ingestTookInMillis,
675706 }
676707 }
677708
678- void markCurrentItemAsDropped ( ) {
679- IndexRequest indexRequest = getIndexWriteRequest (bulkRequest .requests ().get (currentSlot ));
680- failedSlots .set (currentSlot );
709+ synchronized void markItemAsDropped ( int slot ) {
710+ IndexRequest indexRequest = getIndexWriteRequest (bulkRequest .requests ().get (slot ));
711+ failedSlots .set (slot );
681712 itemResponses .add (
682- new BulkItemResponse (currentSlot , indexRequest .opType (),
713+ new BulkItemResponse (slot , indexRequest .opType (),
683714 new UpdateResponse (
684715 new ShardId (indexRequest .index (), IndexMetaData .INDEX_UUID_NA_VALUE , 0 ),
685716 indexRequest .type (), indexRequest .id (), indexRequest .version (), DocWriteResponse .Result .NOOP
@@ -688,16 +719,19 @@ void markCurrentItemAsDropped() {
688719 );
689720 }
690721
691- void markCurrentItemAsFailed (Exception e ) {
692- IndexRequest indexRequest = getIndexWriteRequest (bulkRequest .requests ().get (currentSlot ));
722+ synchronized void markItemAsFailed (int slot , Exception e ) {
723+ IndexRequest indexRequest = getIndexWriteRequest (bulkRequest .requests ().get (slot ));
724+ LOGGER .debug (() -> new ParameterizedMessage ("failed to execute pipeline [{}] for document [{}/{}/{}]" ,
725+ indexRequest .getPipeline (), indexRequest .index (), indexRequest .type (), indexRequest .id ()), e );
726+
693727 // We hit a error during preprocessing a request, so we:
694728 // 1) Remember the request item slot from the bulk, so that we're done processing all requests we know what failed
695729 // 2) Add a bulk item failure for this request
696730 // 3) Continue with the next request in the bulk.
697- failedSlots .set (currentSlot );
731+ failedSlots .set (slot );
698732 BulkItemResponse .Failure failure = new BulkItemResponse .Failure (indexRequest .index (), indexRequest .type (),
699733 indexRequest .id (), e );
700- itemResponses .add (new BulkItemResponse (currentSlot , indexRequest .opType (), failure ));
734+ itemResponses .add (new BulkItemResponse (slot , indexRequest .opType (), failure ));
701735 }
702736
703737 }
0 commit comments