This is an automated email from the ASF dual-hosted git repository.

amatya pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new af05cfa78c Fix shutdown in httpRemote task runner (#13558)
af05cfa78c is described below

commit af05cfa78c10d3b801413b78b5064ef7fda5aa13
Author: AmatyaAvadhanula <[email protected]>
AuthorDate: Thu Dec 22 14:50:04 2022 +0530

    Fix shutdown in httpRemote task runner (#13558)
    
    * Fix shutdown in httpRemote task runner
    
    * Add UT
---
 .../overlord/hrtr/HttpRemoteTaskRunner.java        |   7 +-
 .../druid/indexing/overlord/TaskQueueTest.java     | 140 +++++++++++++++++++++
 .../overlord/hrtr/HttpRemoteTaskRunnerTest.java    |  68 +++++++++-
 3 files changed, 210 insertions(+), 5 deletions(-)

diff --git 
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunner.java
 
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunner.java
index 245933eaf8..6eb1a9c28d 100644
--- 
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunner.java
+++ 
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunner.java
@@ -1310,13 +1310,15 @@ public class HttpRemoteTaskRunner implements 
WorkerTaskRunner, TaskLogStreamer
     WorkerHolder workerHolderRunningTask = null;
     synchronized (statusLock) {
       log.info("Shutdown [%s] because: [%s]", taskId, reason);
-      HttpRemoteTaskRunnerWorkItem taskRunnerWorkItem = tasks.remove(taskId);
+      HttpRemoteTaskRunnerWorkItem taskRunnerWorkItem = tasks.get(taskId);
       if (taskRunnerWorkItem != null) {
         if (taskRunnerWorkItem.getState() == 
HttpRemoteTaskRunnerWorkItem.State.RUNNING) {
           workerHolderRunningTask = 
workers.get(taskRunnerWorkItem.getWorker().getHost());
           if (workerHolderRunningTask == null) {
             log.info("Can't shutdown! No worker running task[%s]", taskId);
           }
+        } else if (taskRunnerWorkItem.getState() == 
HttpRemoteTaskRunnerWorkItem.State.COMPLETE) {
+          tasks.remove(taskId);
         }
       } else {
         log.info("Received shutdown task[%s], but can't find it. Ignored.", 
taskId);
@@ -1466,7 +1468,8 @@ public class HttpRemoteTaskRunner implements 
WorkerTaskRunner, TaskLogStreamer
     return Optional.fromNullable(provisioningService.getStats());
   }
 
-  void taskAddedOrUpdated(final TaskAnnouncement announcement, final 
WorkerHolder workerHolder)
+  @VisibleForTesting
+  public void taskAddedOrUpdated(final TaskAnnouncement announcement, final 
WorkerHolder workerHolder)
   {
     final String taskId = announcement.getTaskId();
     final Worker worker = workerHolder.getWorker();
diff --git 
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskQueueTest.java
 
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskQueueTest.java
index 83df292e31..616d2ca8ba 100644
--- 
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskQueueTest.java
+++ 
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskQueueTest.java
@@ -24,6 +24,11 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.druid.common.guava.DSuppliers;
+import org.apache.druid.discovery.DruidNodeDiscoveryProvider;
+import org.apache.druid.discovery.WorkerNodeService;
+import org.apache.druid.indexer.TaskLocation;
 import org.apache.druid.indexer.TaskState;
 import org.apache.druid.indexer.TaskStatus;
 import org.apache.druid.indexing.common.TaskLock;
@@ -36,19 +41,34 @@ import 
org.apache.druid.indexing.common.task.IngestionTestBase;
 import org.apache.druid.indexing.common.task.Task;
 import org.apache.druid.indexing.common.task.Tasks;
 import 
org.apache.druid.indexing.common.task.batch.parallel.SinglePhaseParallelIndexTaskRunner;
+import org.apache.druid.indexing.overlord.autoscaling.NoopProvisioningStrategy;
 import org.apache.druid.indexing.overlord.autoscaling.ScalingStats;
 import org.apache.druid.indexing.overlord.config.DefaultTaskConfig;
+import org.apache.druid.indexing.overlord.config.HttpRemoteTaskRunnerConfig;
 import org.apache.druid.indexing.overlord.config.TaskLockConfig;
 import org.apache.druid.indexing.overlord.config.TaskQueueConfig;
+import org.apache.druid.indexing.overlord.hrtr.HttpRemoteTaskRunner;
+import org.apache.druid.indexing.overlord.hrtr.HttpRemoteTaskRunnerTest;
+import org.apache.druid.indexing.overlord.hrtr.WorkerHolder;
+import org.apache.druid.indexing.overlord.setup.DefaultWorkerBehaviorConfig;
+import org.apache.druid.indexing.worker.TaskAnnouncement;
+import org.apache.druid.indexing.worker.Worker;
 import org.apache.druid.indexing.worker.config.WorkerConfig;
 import org.apache.druid.java.util.common.Intervals;
 import org.apache.druid.java.util.common.Pair;
 import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.java.util.common.concurrent.Execs;
 import org.apache.druid.java.util.common.granularity.Granularities;
 import org.apache.druid.java.util.common.granularity.Granularity;
+import org.apache.druid.java.util.http.client.HttpClient;
+import org.apache.druid.java.util.metrics.StubServiceEmitter;
 import org.apache.druid.metadata.EntryExistsException;
+import org.apache.druid.segment.TestHelper;
+import org.apache.druid.server.initialization.IndexerZkConfig;
+import org.apache.druid.server.initialization.ZkPathsConfig;
 import org.apache.druid.server.metrics.NoopServiceEmitter;
 import org.apache.druid.timeline.DataSegment;
+import org.easymock.EasyMock;
 import org.joda.time.Interval;
 import org.junit.Assert;
 import org.junit.Test;
@@ -60,6 +80,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 public class TaskQueueTest extends IngestionTestBase
 {
@@ -350,6 +372,124 @@ public class TaskQueueTest extends IngestionTestBase
     );
   }
 
+  @Test
+  public void testKilledTasksEmitRuntimeMetricWithHttpRemote() throws 
EntryExistsException, InterruptedException
+  {
+    final TaskActionClientFactory actionClientFactory = 
createActionClientFactory();
+    final HttpRemoteTaskRunner taskRunner = 
createHttpRemoteTaskRunner(ImmutableList.of("t1"));
+    final StubServiceEmitter metricsVerifier = new 
StubServiceEmitter("druid/overlord", "testHost");
+    WorkerHolder workerHolder = EasyMock.createMock(WorkerHolder.class);
+    EasyMock.expect(workerHolder.getWorker()).andReturn(new Worker("http", 
"worker", "127.0.0.1", 1, "v1", WorkerConfig.DEFAULT_CATEGORY)).anyTimes();
+    workerHolder.incrementContinuouslyFailedTasksCount();
+    EasyMock.expectLastCall();
+    workerHolder.setLastCompletedTaskTime(EasyMock.anyObject());
+    
EasyMock.expect(workerHolder.getContinuouslyFailedTasksCount()).andReturn(1);
+    EasyMock.replay(workerHolder);
+    final TaskQueue taskQueue = new TaskQueue(
+        new TaskLockConfig(),
+        new TaskQueueConfig(null, null, null, null),
+        new DefaultTaskConfig(),
+        getTaskStorage(),
+        taskRunner,
+        actionClientFactory,
+        getLockbox(),
+        metricsVerifier
+    );
+    taskQueue.setActive(true);
+    final Task task = new TestTask(
+        "t1",
+        Intervals.of("2021-01-01/P1D"),
+        ImmutableMap.of(
+            Tasks.FORCE_TIME_CHUNK_LOCK_KEY,
+            false
+        )
+    );
+    taskQueue.add(task);
+    taskQueue.manageInternal();
+    taskRunner.taskAddedOrUpdated(TaskAnnouncement.create(
+        task,
+        TaskStatus.running(task.getId()),
+        TaskLocation.create("worker", 1, 2)
+    ), workerHolder);
+    while (!taskRunner.getRunningTasks()
+                      .stream()
+                      .map(TaskRunnerWorkItem::getTaskId)
+                      .collect(Collectors.toList())
+                      .contains(task.getId())) {
+      Thread.sleep(100);
+    }
+    taskQueue.shutdown(task.getId(), "shutdown");
+    taskRunner.taskAddedOrUpdated(TaskAnnouncement.create(
+        task,
+        TaskStatus.failure(task.getId(), "shutdown"),
+        TaskLocation.create("worker", 1, 2)
+    ), workerHolder);
+    taskQueue.manageInternal();
+
+    metricsVerifier.getEvents();
+    metricsVerifier.verifyEmitted("task/run/time", 1);
+  }
+
+  private HttpRemoteTaskRunner createHttpRemoteTaskRunner(List<String> 
runningTasks)
+  {
+    HttpRemoteTaskRunnerTest.TestDruidNodeDiscovery druidNodeDiscovery = new 
HttpRemoteTaskRunnerTest.TestDruidNodeDiscovery();
+    DruidNodeDiscoveryProvider druidNodeDiscoveryProvider = 
EasyMock.createMock(DruidNodeDiscoveryProvider.class);
+    
EasyMock.expect(druidNodeDiscoveryProvider.getForService(WorkerNodeService.DISCOVERY_SERVICE_KEY))
+            .andReturn(druidNodeDiscovery);
+    EasyMock.replay(druidNodeDiscoveryProvider);
+    TaskStorage taskStorageMock = EasyMock.createStrictMock(TaskStorage.class);
+    for (String taskId : runningTasks) {
+      
EasyMock.expect(taskStorageMock.getStatus(taskId)).andReturn(Optional.of(TaskStatus.running(taskId)));
+    }
+    EasyMock.replay(taskStorageMock);
+    HttpRemoteTaskRunner taskRunner = new HttpRemoteTaskRunner(
+        TestHelper.makeJsonMapper(),
+        new HttpRemoteTaskRunnerConfig()
+        {
+          @Override
+          public int getPendingTasksRunnerNumThreads()
+          {
+            return 3;
+          }
+        },
+        EasyMock.createNiceMock(HttpClient.class),
+        DSuppliers.of(new 
AtomicReference<>(DefaultWorkerBehaviorConfig.defaultConfig())),
+        new NoopProvisioningStrategy<>(),
+        druidNodeDiscoveryProvider,
+        EasyMock.createNiceMock(TaskStorage.class),
+        EasyMock.createNiceMock(CuratorFramework.class),
+        new IndexerZkConfig(new ZkPathsConfig(), null, null, null, null),
+        new StubServiceEmitter("druid/overlord", "testHost")
+    );
+
+    taskRunner.start();
+    taskRunner.registerListener(
+        new TaskRunnerListener()
+        {
+          @Override
+          public String getListenerId()
+          {
+            return "test-listener";
+          }
+
+          @Override
+          public void locationChanged(String taskId, TaskLocation newLocation)
+          {
+            // do nothing
+          }
+
+          @Override
+          public void statusChanged(String taskId, TaskStatus status)
+          {
+            // do nothing
+          }
+        },
+        Execs.directExecutor()
+    );
+
+    return taskRunner;
+  }
+
   private static class TestTask extends AbstractBatchIndexTask
   {
     private final Interval interval;
diff --git 
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerTest.java
 
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerTest.java
index 0c8faeb9ff..e78b78517a 100644
--- 
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerTest.java
+++ 
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerTest.java
@@ -80,6 +80,7 @@ import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 import static org.easymock.EasyMock.isA;
 
@@ -1606,7 +1607,68 @@ public class HttpRemoteTaskRunnerTest
     );
   }
 
-  private HttpRemoteTaskRunner createTaskRunnerForTestTaskAddedOrUpdated(
+  /**
+   * Validate the internal state of tasks within the task runner
+   * when shutdown is called on pending / running tasks and completed tasks
+   */
+  @Test
+  public void testShutdown()
+  {
+    List<Object> listenerNotificationsAccumulator = new ArrayList<>();
+    HttpRemoteTaskRunner taskRunner = 
createTaskRunnerForTestTaskAddedOrUpdated(
+        EasyMock.createStrictMock(TaskStorage.class),
+        listenerNotificationsAccumulator
+    );
+
+    Worker worker = new Worker("http", "localhost", "127.0.0.1", 1, "v1", 
WorkerConfig.DEFAULT_CATEGORY);
+
+    WorkerHolder workerHolder = EasyMock.createMock(WorkerHolder.class);
+    EasyMock.expect(workerHolder.getWorker()).andReturn(worker).anyTimes();
+    workerHolder.setLastCompletedTaskTime(EasyMock.anyObject());
+    workerHolder.resetContinuouslyFailedTasksCount();
+    
EasyMock.expect(workerHolder.getContinuouslyFailedTasksCount()).andReturn(0);
+    EasyMock.replay(workerHolder);
+
+    taskRunner.start();
+
+    Task pendingTask = NoopTask.create("pendingTask");
+    taskRunner.run(pendingTask);
+    // Pending task is not cleaned up immediately
+    taskRunner.shutdown(pendingTask.getId(), "Forced shutdown");
+    Assert.assertTrue(taskRunner.getKnownTasks()
+                                .stream()
+                                .map(TaskRunnerWorkItem::getTaskId)
+                                .collect(Collectors.toSet())
+                                .contains(pendingTask.getId())
+    );
+
+    Task completedTask = NoopTask.create("completedTask");
+    taskRunner.run(completedTask);
+    taskRunner.taskAddedOrUpdated(TaskAnnouncement.create(
+        completedTask,
+        TaskStatus.success(completedTask.getId()),
+        TaskLocation.create("worker", 1, 2)
+    ), workerHolder);
+    Assert.assertEquals(completedTask.getId(), 
Iterables.getOnlyElement(taskRunner.getCompletedTasks()).getTaskId());
+    TestDruidNodeDiscovery druidNodeDiscovery = new TestDruidNodeDiscovery();
+    DruidNodeDiscoveryProvider druidNodeDiscoveryProvider = 
EasyMock.createMock(DruidNodeDiscoveryProvider.class);
+    
EasyMock.expect(druidNodeDiscoveryProvider.getForService(WorkerNodeService.DISCOVERY_SERVICE_KEY))
+            .andReturn(druidNodeDiscovery);
+    EasyMock.replay(druidNodeDiscoveryProvider);
+
+
+    // Completed tasks are cleaned up when shutdown is invokded on them (by 
TaskQueue)
+    taskRunner.shutdown(completedTask.getId(), "Cleanup");
+    Assert.assertFalse(taskRunner.getKnownTasks()
+                                .stream()
+                                .map(TaskRunnerWorkItem::getTaskId)
+                                .collect(Collectors.toSet())
+                                .contains(completedTask.getId())
+    );
+
+  }
+
+  public static HttpRemoteTaskRunner createTaskRunnerForTestTaskAddedOrUpdated(
       TaskStorage taskStorage,
       List<Object> listenerNotificationsAccumulator
   )
@@ -1837,7 +1899,7 @@ public class HttpRemoteTaskRunnerTest
     };
   }
 
-  private static class TestDruidNodeDiscovery implements DruidNodeDiscovery
+  public static class TestDruidNodeDiscovery implements DruidNodeDiscovery
   {
     private List<Listener> listeners;
 
@@ -1872,7 +1934,7 @@ public class HttpRemoteTaskRunnerTest
     }
   }
 
-  private interface CustomFunction
+  public interface CustomFunction
   {
     WorkerHolder apply(
         ObjectMapper smileMapper,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to