@@ -494,6 +494,212 @@ public void testSegments() throws Exception {
494494 }
495495 }
496496
497+ public void testMergeSegmentsOnCommitIsDisabled () throws Exception {
498+ final AtomicLong globalCheckpoint = new AtomicLong (SequenceNumbers .NO_OPS_PERFORMED );
499+
500+ final Settings .Builder settings = Settings .builder ()
501+ .put (defaultSettings .getSettings ())
502+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_MAX_FULL_FLUSH_MERGE_WAIT_TIME .getKey (), TimeValue .timeValueMillis (0 ))
503+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_ENABLED .getKey (), true );
504+ final IndexMetadata indexMetadata = IndexMetadata .builder (defaultSettings .getIndexMetadata ()).settings (settings ).build ();
505+ final IndexSettings indexSettings = IndexSettingsModule .newIndexSettings (indexMetadata );
506+
507+ try (
508+ Store store = createStore ();
509+ InternalEngine engine = createEngine (
510+ config (indexSettings , store , createTempDir (), NoMergePolicy .INSTANCE , null , null , globalCheckpoint ::get )
511+ )
512+ ) {
513+ assertThat (engine .segments (false ), empty ());
514+ int numDocsFirstSegment = randomIntBetween (5 , 50 );
515+ Set <String > liveDocsFirstSegment = new HashSet <>();
516+ for (int i = 0 ; i < numDocsFirstSegment ; i ++) {
517+ String id = Integer .toString (i );
518+ ParsedDocument doc = testParsedDocument (id , null , testDocument (), B_1 , null );
519+ engine .index (indexForDoc (doc ));
520+ liveDocsFirstSegment .add (id );
521+ }
522+ engine .refresh ("test" );
523+ List <Segment > segments = engine .segments (randomBoolean ());
524+ assertThat (segments , hasSize (1 ));
525+ assertThat (segments .get (0 ).getNumDocs (), equalTo (liveDocsFirstSegment .size ()));
526+ assertThat (segments .get (0 ).getDeletedDocs (), equalTo (0 ));
527+ assertFalse (segments .get (0 ).committed );
528+ int deletes = 0 ;
529+ int updates = 0 ;
530+ int appends = 0 ;
531+ int iterations = scaledRandomIntBetween (1 , 50 );
532+ for (int i = 0 ; i < iterations && liveDocsFirstSegment .isEmpty () == false ; i ++) {
533+ String idToUpdate = randomFrom (liveDocsFirstSegment );
534+ liveDocsFirstSegment .remove (idToUpdate );
535+ ParsedDocument doc = testParsedDocument (idToUpdate , null , testDocument (), B_1 , null );
536+ if (randomBoolean ()) {
537+ engine .delete (new Engine .Delete (doc .id (), newUid (doc ), primaryTerm .get ()));
538+ deletes ++;
539+ } else {
540+ engine .index (indexForDoc (doc ));
541+ updates ++;
542+ }
543+ if (randomBoolean ()) {
544+ engine .index (indexForDoc (testParsedDocument (UUIDs .randomBase64UUID (), null , testDocument (), B_1 , null )));
545+ appends ++;
546+ }
547+ }
548+
549+ boolean committed = randomBoolean ();
550+ if (committed ) {
551+ engine .flush ();
552+ }
553+
554+ engine .refresh ("test" );
555+ segments = engine .segments (randomBoolean ());
556+
557+ assertThat (segments , hasSize (2 ));
558+ assertThat (segments , hasSize (2 ));
559+ assertThat (segments .get (0 ).getNumDocs (), equalTo (liveDocsFirstSegment .size ()));
560+ assertThat (segments .get (0 ).getDeletedDocs (), equalTo (updates + deletes ));
561+ assertThat (segments .get (0 ).committed , equalTo (committed ));
562+
563+ assertThat (segments .get (1 ).getNumDocs (), equalTo (updates + appends ));
564+ assertThat (segments .get (1 ).getDeletedDocs (), equalTo (deletes )); // delete tombstones
565+ assertThat (segments .get (1 ).committed , equalTo (committed ));
566+ }
567+ }
568+
569+ public void testMergeSegmentsOnCommit () throws Exception {
570+ final AtomicLong globalCheckpoint = new AtomicLong (SequenceNumbers .NO_OPS_PERFORMED );
571+
572+ final Settings .Builder settings = Settings .builder ()
573+ .put (defaultSettings .getSettings ())
574+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_MAX_FULL_FLUSH_MERGE_WAIT_TIME .getKey (), TimeValue .timeValueMillis (5000 ))
575+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_ENABLED .getKey (), true );
576+ final IndexMetadata indexMetadata = IndexMetadata .builder (defaultSettings .getIndexMetadata ()).settings (settings ).build ();
577+ final IndexSettings indexSettings = IndexSettingsModule .newIndexSettings (indexMetadata );
578+
579+ try (
580+ Store store = createStore ();
581+ InternalEngine engine = createEngine (
582+ config (indexSettings , store , createTempDir (), NoMergePolicy .INSTANCE , null , null , globalCheckpoint ::get )
583+ )
584+ ) {
585+ assertThat (engine .segments (false ), empty ());
586+ int numDocsFirstSegment = randomIntBetween (5 , 50 );
587+ Set <String > liveDocsFirstSegment = new HashSet <>();
588+ for (int i = 0 ; i < numDocsFirstSegment ; i ++) {
589+ String id = Integer .toString (i );
590+ ParsedDocument doc = testParsedDocument (id , null , testDocument (), B_1 , null );
591+ engine .index (indexForDoc (doc ));
592+ liveDocsFirstSegment .add (id );
593+ }
594+ engine .refresh ("test" );
595+ List <Segment > segments = engine .segments (randomBoolean ());
596+ assertThat (segments , hasSize (1 ));
597+ assertThat (segments .get (0 ).getNumDocs (), equalTo (liveDocsFirstSegment .size ()));
598+ assertThat (segments .get (0 ).getDeletedDocs (), equalTo (0 ));
599+ assertFalse (segments .get (0 ).committed );
600+ int deletes = 0 ;
601+ int updates = 0 ;
602+ int appends = 0 ;
603+ int iterations = scaledRandomIntBetween (1 , 50 );
604+ for (int i = 0 ; i < iterations && liveDocsFirstSegment .isEmpty () == false ; i ++) {
605+ String idToUpdate = randomFrom (liveDocsFirstSegment );
606+ liveDocsFirstSegment .remove (idToUpdate );
607+ ParsedDocument doc = testParsedDocument (idToUpdate , null , testDocument (), B_1 , null );
608+ if (randomBoolean ()) {
609+ engine .delete (new Engine .Delete (doc .id (), newUid (doc ), primaryTerm .get ()));
610+ deletes ++;
611+ } else {
612+ engine .index (indexForDoc (doc ));
613+ updates ++;
614+ }
615+ if (randomBoolean ()) {
616+ engine .index (indexForDoc (testParsedDocument (UUIDs .randomBase64UUID (), null , testDocument (), B_1 , null )));
617+ appends ++;
618+ }
619+ }
620+
621+ boolean committed = randomBoolean ();
622+ if (committed ) {
623+ engine .flush ();
624+ }
625+
626+ engine .refresh ("test" );
627+ segments = engine .segments (randomBoolean ());
628+
629+ // All segments have to be merged into one
630+ assertThat (segments , hasSize (1 ));
631+ assertThat (segments .get (0 ).getNumDocs (), equalTo (numDocsFirstSegment + appends - deletes ));
632+ assertThat (segments .get (0 ).getDeletedDocs (), equalTo (0 ));
633+ assertThat (segments .get (0 ).committed , equalTo (committed ));
634+ }
635+ }
636+
637+ // this test writes documents to the engine while concurrently flushing/commit
638+ public void testConcurrentMergeSegmentsOnCommit () throws Exception {
639+ final AtomicLong globalCheckpoint = new AtomicLong (SequenceNumbers .NO_OPS_PERFORMED );
640+
641+ final Settings .Builder settings = Settings .builder ()
642+ .put (defaultSettings .getSettings ())
643+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_MAX_FULL_FLUSH_MERGE_WAIT_TIME .getKey (), TimeValue .timeValueMillis (5000 ))
644+ .put (IndexSettings .INDEX_MERGE_ON_FLUSH_ENABLED .getKey (), true );
645+ final IndexMetadata indexMetadata = IndexMetadata .builder (defaultSettings .getIndexMetadata ()).settings (settings ).build ();
646+ final IndexSettings indexSettings = IndexSettingsModule .newIndexSettings (indexMetadata );
647+
648+ try (
649+ Store store = createStore ();
650+ InternalEngine engine = createEngine (
651+ config (indexSettings , store , createTempDir (), NoMergePolicy .INSTANCE , null , null , globalCheckpoint ::get )
652+ )
653+ ) {
654+ final int numIndexingThreads = scaledRandomIntBetween (3 , 8 );
655+ final int numDocsPerThread = randomIntBetween (500 , 1000 );
656+ final CyclicBarrier barrier = new CyclicBarrier (numIndexingThreads + 1 );
657+ final List <Thread > indexingThreads = new ArrayList <>();
658+ final CountDownLatch doneLatch = new CountDownLatch (numIndexingThreads );
659+ // create N indexing threads to index documents simultaneously
660+ for (int threadNum = 0 ; threadNum < numIndexingThreads ; threadNum ++) {
661+ final int threadIdx = threadNum ;
662+ Thread indexingThread = new Thread (() -> {
663+ try {
664+ barrier .await (); // wait for all threads to start at the same time
665+ // index random number of docs
666+ for (int i = 0 ; i < numDocsPerThread ; i ++) {
667+ final String id = "thread" + threadIdx + "#" + i ;
668+ ParsedDocument doc = testParsedDocument (id , null , testDocument (), B_1 , null );
669+ engine .index (indexForDoc (doc ));
670+ }
671+ } catch (Exception e ) {
672+ throw new RuntimeException (e );
673+ } finally {
674+ doneLatch .countDown ();
675+ }
676+
677+ });
678+ indexingThreads .add (indexingThread );
679+ }
680+
681+ // start the indexing threads
682+ for (Thread thread : indexingThreads ) {
683+ thread .start ();
684+ }
685+ barrier .await (); // wait for indexing threads to all be ready to start
686+ assertThat (doneLatch .await (10 , TimeUnit .SECONDS ), is (true ));
687+
688+ boolean committed = randomBoolean ();
689+ if (committed ) {
690+ engine .flush ();
691+ }
692+
693+ engine .refresh ("test" );
694+ List <Segment > segments = engine .segments (randomBoolean ());
695+
696+ // All segments have to be merged into one
697+ assertThat (segments , hasSize (1 ));
698+ assertThat (segments .get (0 ).getNumDocs (), equalTo (numIndexingThreads * numDocsPerThread ));
699+ assertThat (segments .get (0 ).committed , equalTo (committed ));
700+ }
701+ }
702+
497703 public void testCommitStats () throws IOException {
498704 final AtomicLong maxSeqNo = new AtomicLong (SequenceNumbers .NO_OPS_PERFORMED );
499705 final AtomicLong localCheckpoint = new AtomicLong (SequenceNumbers .NO_OPS_PERFORMED );
0 commit comments