1414import org .elasticsearch .common .util .concurrent .DeterministicTaskQueue ;
1515import org .elasticsearch .common .util .concurrent .EsExecutors ;
1616import org .elasticsearch .index .IndexSettings ;
17+ import org .elasticsearch .index .engine .ThreadPoolMergeScheduler .MergeTask ;
1718import org .elasticsearch .test .ESTestCase ;
1819import org .elasticsearch .test .IndexSettingsModule ;
1920import org .elasticsearch .threadpool .TestThreadPool ;
2021import org .elasticsearch .threadpool .ThreadPool ;
2122import org .junit .Before ;
2223
2324import java .util .Collection ;
25+ import java .util .HashSet ;
26+ import java .util .PriorityQueue ;
27+ import java .util .Set ;
2428import java .util .concurrent .CountDownLatch ;
29+ import java .util .concurrent .atomic .AtomicReference ;
30+ import java .util .function .Consumer ;
2531
2632import static org .hamcrest .Matchers .equalTo ;
33+ import static org .mockito .ArgumentMatchers .any ;
2734import static org .mockito .ArgumentMatchers .anyDouble ;
2835import static org .mockito .Mockito .doAnswer ;
2936import static org .mockito .Mockito .mock ;
@@ -55,7 +62,7 @@ public void testMergeTasksAreAbortedWhenThreadPoolIsShutdown() {
5562 assertTrue (threadPoolMergeExecutorService .allDone ());
5663 // shutdown the thread pool
5764 testThreadPool .shutdown ();
58- ThreadPoolMergeScheduler . MergeTask mergeTask = mock (ThreadPoolMergeScheduler . MergeTask .class );
65+ MergeTask mergeTask = mock (MergeTask .class );
5966 when (mergeTask .isRunning ()).thenReturn (false );
6067 boolean mergeTaskSupportsIOThrottling = randomBoolean ();
6168 when (mergeTask .supportsIOThrottling ()).thenReturn (mergeTaskSupportsIOThrottling );
@@ -82,10 +89,10 @@ public void testBackloggedMergeTasksAreAllExecutedExactlyOnce() throws Exception
8289 CountDownLatch mergeTasksDoneLatch = new CountDownLatch (mergeTaskCount );
8390 CountDownLatch mergeTasksReadyLatch = new CountDownLatch (mergeTaskCount );
8491 CountDownLatch submitTaskLatch = new CountDownLatch (1 );
85- Collection <ThreadPoolMergeScheduler . MergeTask > generatedMergeTasks = ConcurrentCollections .newConcurrentSet ();
92+ Collection <MergeTask > generatedMergeTasks = ConcurrentCollections .newConcurrentSet ();
8693 for (int i = 0 ; i < mergeTaskCount ; i ++) {
8794 new Thread (() -> {
88- ThreadPoolMergeScheduler . MergeTask mergeTask = mock (ThreadPoolMergeScheduler . MergeTask .class );
95+ MergeTask mergeTask = mock (MergeTask .class );
8996 when (mergeTask .isRunning ()).thenReturn (false );
9097 boolean supportsIOThrottling = randomBoolean ();
9198 when (mergeTask .supportsIOThrottling ()).thenReturn (supportsIOThrottling );
@@ -115,7 +122,7 @@ public void testBackloggedMergeTasksAreAllExecutedExactlyOnce() throws Exception
115122 submitTaskLatch .countDown ();
116123 safeAwait (mergeTasksDoneLatch );
117124 assertBusy (() -> {
118- for (ThreadPoolMergeScheduler . MergeTask mergeTask : generatedMergeTasks ) {
125+ for (MergeTask mergeTask : generatedMergeTasks ) {
119126 verify (mergeTask , times (1 )).run ();
120127 if (mergeTask .supportsIOThrottling ()) {
121128 verify (mergeTask ).setIORateLimit (anyDouble ());
@@ -127,4 +134,58 @@ public void testBackloggedMergeTasksAreAllExecutedExactlyOnce() throws Exception
127134 });
128135 }
129136 }
137+
138+ public void testMergeTasksRunInSizeOrderWithBacklog () {
139+ ThreadPoolMergeExecutorService threadPoolMergeExecutorService = ThreadPoolMergeExecutorService
140+ .maybeCreateThreadPoolMergeExecutorService (
141+ testThreadPool ,
142+ Settings .builder ().put (ThreadPoolMergeScheduler .USE_THREAD_POOL_MERGE_SCHEDULER_SETTING .getKey (), true ).build ()
143+ );
144+ assertNotNull (threadPoolMergeExecutorService );
145+ threadPoolMergeExecutorService .submitMergeTask ()
146+ // int mergeTaskCount = randomIntBetween(5, 50);
147+ int mergeTaskCount = 4 ;
148+ PriorityQueue <MergeTask > mergeTasksStillToRun = new PriorityQueue <>();
149+ for (int i = 0 ; i < mergeTaskCount ; i ++) {
150+ MergeTask mergeTask = mock (MergeTask .class );
151+ when (mergeTask .isRunning ()).thenReturn (false );
152+ boolean supportsIOThrottling = randomBoolean ();
153+ when (mergeTask .supportsIOThrottling ()).thenReturn (supportsIOThrottling );
154+ long mergeSize = randomLongBetween (1 , 10 );
155+ when (mergeTask .estimatedMergeSize ()).thenReturn (mergeSize );
156+ doAnswer (mock -> {
157+ @ SuppressWarnings ("unchecked" )
158+ MergeTask other = (MergeTask ) mock .getArguments ()[1 ];
159+ return Long .com
160+ }).when (mergeTask ).compareTo (any (MergeTask .class ));
161+ doAnswer (mock -> {
162+ mergeTasksStillToRun .remove (mergeTask );
163+ // each individual merge task can either "run" or be "backlogged"
164+ boolean runNowOrBacklog = randomBoolean ();
165+ if (runNowOrBacklog == false ) {
166+ if (mergeTasksStillToRun .isEmpty ()) {
167+ // reenqueue backlogged merge task now, otherwise the task won't finish
168+ threadPoolMergeExecutorService .reEnqueueBackloggedMergeTask (mergeTask );
169+ mergeTasksStillToRun .add (mergeTask );
170+ } else {
171+ testThreadPool .executor (ThreadPool .Names .GENERIC ).execute (() -> {
172+ // reenqueue backlogged merge task sometime in the future
173+ threadPoolMergeExecutorService .reEnqueueBackloggedMergeTask (mergeTask );
174+ mergeTasksStillToRun .add (mergeTask );
175+ });
176+ }
177+ }
178+ return runNowOrBacklog ;
179+ }).when (mergeTask ).runNowOrBacklog ();
180+ doAnswer (mock -> {
181+ if (mergeTasksStillToRun .isEmpty () == false ) {
182+ assertTrue (mergeTask .estimatedMergeSize () <= mergeTasksStillToRun .peek ().estimatedMergeSize ());
183+ }
184+ return null ;
185+ }).when (mergeTask ).run ();
186+ mergeTasksStillToRun .add (mergeTask );
187+ threadPoolMergeExecutorService .submitMergeTask (mergeTask );
188+ }
189+ deterministicTaskQueue .runAllTasks ();
190+ }
130191}
0 commit comments