Wenjun Ruan
10 months ago
committed by
GitHub
49 changed files with 1453 additions and 918 deletions
@ -0,0 +1,62 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroup; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface TaskGroupDao extends IDao<TaskGroup> { |
||||
|
||||
/** |
||||
* Query all TaskGroups |
||||
* |
||||
* @return all TaskGroups |
||||
*/ |
||||
List<TaskGroup> queryAllTaskGroups(); |
||||
|
||||
/** |
||||
* Query all TaskGroups which useSize > 0 |
||||
* |
||||
* @return the TaskGroups which useSize > 0 |
||||
*/ |
||||
List<TaskGroup> queryUsedTaskGroups(); |
||||
|
||||
/** |
||||
* Query all TaskGroups which useSize < groupSize |
||||
* |
||||
* @return the TaskGroups which useSize < groupSize |
||||
*/ |
||||
List<TaskGroup> queryAvailableTaskGroups(); |
||||
|
||||
/** |
||||
* Acquire a slot for the TaskGroup which useSize should < groupSize, set the useSize = useSize + 1. |
||||
* |
||||
* @param taskGroupId taskGroupId which shouldn't be null |
||||
* @return true if acquire successfully, false otherwise. |
||||
*/ |
||||
boolean acquireTaskGroupSlot(Integer taskGroupId); |
||||
|
||||
/** |
||||
* Release a slot for the TaskGroup which useSize should > 0, set the useSize = useSize - 1. |
||||
* |
||||
* @param taskGroupId taskGroupId which shouldn't be null |
||||
* @return true if release successfully, false otherwise. |
||||
*/ |
||||
boolean releaseTaskGroupSlot(Integer taskGroupId); |
||||
} |
@ -0,0 +1,64 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface TaskGroupQueueDao extends IDao<TaskGroupQueue> { |
||||
|
||||
/** |
||||
* Delete {@link TaskGroupQueue} by {@link ProcessInstance#getId()} |
||||
* |
||||
* @param workflowInstanceIds workflowInstanceIds |
||||
*/ |
||||
void deleteByWorkflowInstanceIds(List<Integer> workflowInstanceIds); |
||||
|
||||
/** |
||||
* Query all {@link TaskGroupQueue} which in_queue is {@link org.apache.dolphinscheduler.common.enums.Flag#YES} |
||||
* |
||||
* @return TaskGroupQueue ordered by priority desc |
||||
*/ |
||||
List<TaskGroupQueue> queryAllInQueueTaskGroupQueue(); |
||||
|
||||
/** |
||||
* Query all {@link TaskGroupQueue} which in_queue is {@link org.apache.dolphinscheduler.common.enums.Flag#YES} and taskGroupId is taskGroupId |
||||
* |
||||
* @param taskGroupId taskGroupId |
||||
* @return TaskGroupQueue ordered by priority desc |
||||
*/ |
||||
List<TaskGroupQueue> queryAllInQueueTaskGroupQueueByGroupId(Integer taskGroupId); |
||||
|
||||
/** |
||||
* Query all {@link TaskGroupQueue} which taskId is taskInstanceId |
||||
* |
||||
* @param taskInstanceId taskInstanceId |
||||
* @return TaskGroupQueue ordered by priority desc |
||||
*/ |
||||
List<TaskGroupQueue> queryByTaskInstanceId(Integer taskInstanceId); |
||||
|
||||
/** |
||||
* Query all {@link TaskGroupQueue} which status is TaskGroupQueueStatus.ACQUIRE_SUCCESS and forceStart is {@link org.apache.dolphinscheduler.common.enums.Flag#NO}. |
||||
* |
||||
* @param taskGroupId taskGroupId |
||||
* @return TaskGroupQueue |
||||
*/ |
||||
List<TaskGroupQueue> queryAcquiredTaskGroupQueueByGroupId(Integer taskGroupId); |
||||
} |
@ -0,0 +1,70 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository.impl; |
||||
|
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroup; |
||||
import org.apache.dolphinscheduler.dao.mapper.TaskGroupMapper; |
||||
import org.apache.dolphinscheduler.dao.repository.BaseDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupDao; |
||||
|
||||
import java.util.List; |
||||
|
||||
import lombok.NonNull; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import org.springframework.stereotype.Repository; |
||||
|
||||
@Slf4j |
||||
@Repository |
||||
public class TaskGroupDaoImpl extends BaseDao<TaskGroup, TaskGroupMapper> implements TaskGroupDao { |
||||
|
||||
public TaskGroupDaoImpl(@NonNull TaskGroupMapper taskGroupMapper) { |
||||
super(taskGroupMapper); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroup> queryAllTaskGroups() { |
||||
return mybatisMapper.selectList(null); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroup> queryUsedTaskGroups() { |
||||
return mybatisMapper.queryUsedTaskGroups(); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroup> queryAvailableTaskGroups() { |
||||
return mybatisMapper.queryAvailableTaskGroups(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean acquireTaskGroupSlot(Integer taskGroupId) { |
||||
if (taskGroupId == null) { |
||||
throw new IllegalArgumentException("taskGroupId cannot be null"); |
||||
} |
||||
return mybatisMapper.acquireTaskGroupSlot(taskGroupId) > 0; |
||||
} |
||||
|
||||
@Override |
||||
public boolean releaseTaskGroupSlot(Integer taskGroupId) { |
||||
if (taskGroupId == null) { |
||||
throw new IllegalArgumentException("taskGroupId cannot be null"); |
||||
} |
||||
return mybatisMapper.releaseTaskGroupSlot(taskGroupId) > 0; |
||||
} |
||||
} |
@ -0,0 +1,73 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository.impl; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.Flag; |
||||
import org.apache.dolphinscheduler.common.enums.TaskGroupQueueStatus; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue; |
||||
import org.apache.dolphinscheduler.dao.mapper.TaskGroupQueueMapper; |
||||
import org.apache.dolphinscheduler.dao.repository.BaseDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupQueueDao; |
||||
|
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
|
||||
import java.util.List; |
||||
|
||||
import lombok.NonNull; |
||||
|
||||
import org.springframework.stereotype.Repository; |
||||
|
||||
@Repository |
||||
public class TaskGroupQueueDaoImpl extends BaseDao<TaskGroupQueue, TaskGroupQueueMapper> implements TaskGroupQueueDao { |
||||
|
||||
public TaskGroupQueueDaoImpl(@NonNull TaskGroupQueueMapper taskGroupQueueMapper) { |
||||
super(taskGroupQueueMapper); |
||||
} |
||||
|
||||
@Override |
||||
public void deleteByWorkflowInstanceIds(List<Integer> workflowInstanceIds) { |
||||
if (CollectionUtils.isEmpty(workflowInstanceIds)) { |
||||
return; |
||||
} |
||||
mybatisMapper.deleteByWorkflowInstanceIds(workflowInstanceIds); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroupQueue> queryAllInQueueTaskGroupQueue() { |
||||
return mybatisMapper.queryAllTaskGroupQueueByInQueue(Flag.YES.getCode()); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroupQueue> queryAllInQueueTaskGroupQueueByGroupId(Integer taskGroupId) { |
||||
return mybatisMapper.queryAllInQueueTaskGroupQueueByGroupId(taskGroupId, Flag.YES.getCode()); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroupQueue> queryByTaskInstanceId(Integer taskInstanceId) { |
||||
return mybatisMapper.queryByTaskInstanceId(taskInstanceId); |
||||
} |
||||
|
||||
@Override |
||||
public List<TaskGroupQueue> queryAcquiredTaskGroupQueueByGroupId(Integer taskGroupId) { |
||||
return mybatisMapper.queryUsingTaskGroupQueueByGroupId( |
||||
taskGroupId, |
||||
TaskGroupQueueStatus.ACQUIRE_SUCCESS.getCode(), |
||||
Flag.YES.getCode(), |
||||
Flag.NO.getCode()); |
||||
} |
||||
} |
@ -0,0 +1,118 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository.impl; |
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
import static org.junit.jupiter.api.Assertions.assertFalse; |
||||
import static org.junit.jupiter.api.Assertions.assertTrue; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.Flag; |
||||
import org.apache.dolphinscheduler.dao.BaseDaoTest; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroup; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupDao; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
class TaskGroupDaoImplTest extends BaseDaoTest { |
||||
|
||||
@Autowired |
||||
private TaskGroupDao taskGroupDao; |
||||
|
||||
@Test |
||||
void queryAllTaskGroups() { |
||||
TaskGroup taskGroup = createTaskGroup("test", 0, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
List<TaskGroup> taskGroups = taskGroupDao.queryAllTaskGroups(); |
||||
assertEquals(1, taskGroups.size()); |
||||
} |
||||
|
||||
@Test |
||||
void queryUsedTaskGroups() { |
||||
// Insert a unused task group
|
||||
TaskGroup taskGroup = createTaskGroup("testUnused", 0, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertEquals(0, taskGroupDao.queryUsedTaskGroups().size()); |
||||
|
||||
// Insert a used task group
|
||||
taskGroup = createTaskGroup("testUsed", 1, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertEquals(1, taskGroupDao.queryUsedTaskGroups().size()); |
||||
} |
||||
|
||||
@Test |
||||
void queryAvailableTaskGroups() { |
||||
// Insert a full task group
|
||||
TaskGroup taskGroup = createTaskGroup("testFull", 1, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertEquals(0, taskGroupDao.queryAvailableTaskGroups().size()); |
||||
|
||||
// Insert a used task group
|
||||
taskGroup = createTaskGroup("testNotFull", 0, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertEquals(1, taskGroupDao.queryAvailableTaskGroups().size()); |
||||
} |
||||
|
||||
@Test |
||||
void acquireTaskGroupSlot() { |
||||
// Insert a full task group will acquire failed
|
||||
TaskGroup taskGroup = createTaskGroup("testFull", 1, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertFalse(taskGroupDao.acquireTaskGroupSlot(taskGroup.getId())); |
||||
|
||||
taskGroup.setUseSize(0); |
||||
taskGroupDao.updateById(taskGroup); |
||||
assertTrue(taskGroupDao.acquireTaskGroupSlot(taskGroup.getId())); |
||||
|
||||
taskGroup = taskGroupDao.queryById(taskGroup.getId()); |
||||
assertEquals(1, taskGroup.getUseSize()); |
||||
} |
||||
|
||||
@Test |
||||
void releaseTaskGroupSlot() { |
||||
// Insert an empty task group will release failed
|
||||
TaskGroup taskGroup = createTaskGroup("testEmpty", 0, 1); |
||||
taskGroupDao.insert(taskGroup); |
||||
assertFalse(taskGroupDao.releaseTaskGroupSlot(taskGroup.getId())); |
||||
|
||||
taskGroup.setUseSize(1); |
||||
taskGroupDao.updateById(taskGroup); |
||||
assertTrue(taskGroupDao.releaseTaskGroupSlot(taskGroup.getId())); |
||||
|
||||
taskGroup = taskGroupDao.queryById(taskGroup.getId()); |
||||
assertEquals(0, taskGroup.getUseSize()); |
||||
} |
||||
|
||||
private TaskGroup createTaskGroup(String name, int useSize, int groupSize) { |
||||
return TaskGroup.builder() |
||||
.name(name) |
||||
.description("test") |
||||
.groupSize(groupSize) |
||||
.useSize(useSize) |
||||
.userId(1) |
||||
.status(Flag.YES) |
||||
.createTime(new Date()) |
||||
.updateTime(new Date()) |
||||
.projectCode(1) |
||||
.build(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.dao.repository.impl; |
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
import static org.junit.jupiter.api.Assertions.assertNotNull; |
||||
import static org.junit.jupiter.api.Assertions.assertNull; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.Flag; |
||||
import org.apache.dolphinscheduler.common.enums.TaskGroupQueueStatus; |
||||
import org.apache.dolphinscheduler.dao.BaseDaoTest; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupQueueDao; |
||||
|
||||
import java.util.Date; |
||||
|
||||
import org.assertj.core.util.Lists; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
class TaskGroupQueueDaoImplTest extends BaseDaoTest { |
||||
|
||||
@Autowired |
||||
private TaskGroupQueueDao taskGroupQueueDao; |
||||
|
||||
@Test |
||||
void deleteByWorkflowInstanceIds() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertNotNull(taskGroupQueueDao.queryById(taskGroupQueue.getId())); |
||||
|
||||
taskGroupQueueDao.deleteByWorkflowInstanceIds(Lists.newArrayList(1)); |
||||
assertNull(taskGroupQueueDao.queryById(taskGroupQueue.getId())); |
||||
} |
||||
|
||||
@Test |
||||
void queryAllInQueueTaskGroupQueue() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertEquals(1, taskGroupQueueDao.queryAllInQueueTaskGroupQueue().size()); |
||||
} |
||||
|
||||
@Test |
||||
void queryAllInQueueTaskGroupQueueByGroupId() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertEquals(1, taskGroupQueueDao.queryAllInQueueTaskGroupQueueByGroupId(1).size()); |
||||
} |
||||
|
||||
@Test |
||||
void updateById() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.WAIT_QUEUE); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
|
||||
taskGroupQueue.setStatus(TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.updateById(taskGroupQueue); |
||||
assertEquals(TaskGroupQueueStatus.ACQUIRE_SUCCESS, |
||||
taskGroupQueueDao.queryById(taskGroupQueue.getId()).getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
void queryByTaskInstanceId() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertEquals(1, taskGroupQueueDao.queryByTaskInstanceId(1).size()); |
||||
} |
||||
|
||||
@Test |
||||
void queryUsingTaskGroupQueueByGroupId() { |
||||
TaskGroupQueue taskGroupQueue = createTaskGroupQueue(Flag.NO, TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertEquals(1, taskGroupQueueDao.queryAcquiredTaskGroupQueueByGroupId(1).size()); |
||||
|
||||
taskGroupQueue = createTaskGroupQueue(Flag.YES, TaskGroupQueueStatus.WAIT_QUEUE); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
assertEquals(1, taskGroupQueueDao.queryAcquiredTaskGroupQueueByGroupId(1).size()); |
||||
} |
||||
|
||||
private TaskGroupQueue createTaskGroupQueue(Flag forceStart, TaskGroupQueueStatus taskGroupQueueStatus) { |
||||
return TaskGroupQueue.builder() |
||||
.taskId(1) |
||||
.taskName("test") |
||||
.groupId(1) |
||||
.processId(1) |
||||
.priority(0) |
||||
.forceStart(forceStart.getCode()) |
||||
.inQueue(Flag.YES.getCode()) |
||||
.status(taskGroupQueueStatus) |
||||
.createTime(new Date()) |
||||
.updateTime(new Date()) |
||||
.build(); |
||||
} |
||||
} |
@ -1,42 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.extract.master.transportor; |
||||
|
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
public class TaskInstanceForceStartRequest { |
||||
|
||||
private String key; |
||||
|
||||
private int processInstanceId; |
||||
|
||||
private int taskInstanceId; |
||||
|
||||
public TaskInstanceForceStartRequest( |
||||
int processInstanceId, |
||||
int taskInstanceId) { |
||||
this.key = String.format("%d-%d", processInstanceId, taskInstanceId); |
||||
|
||||
this.processInstanceId = processInstanceId; |
||||
this.taskInstanceId = taskInstanceId; |
||||
} |
||||
|
||||
} |
@ -1,41 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.extract.master.transportor; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class TaskInstanceForceStartResponse { |
||||
|
||||
private boolean success; |
||||
|
||||
private String message; |
||||
|
||||
public static TaskInstanceForceStartResponse success() { |
||||
return new TaskInstanceForceStartResponse(true, "dispatch success"); |
||||
} |
||||
|
||||
public static TaskInstanceForceStartResponse failed(String message) { |
||||
return new TaskInstanceForceStartResponse(false, message); |
||||
} |
||||
|
||||
} |
@ -1,47 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.event; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.StateEventType; |
||||
import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import com.google.auto.service.AutoService; |
||||
|
||||
@AutoService(StateEventHandler.class) |
||||
@Slf4j |
||||
public class TaskWaitTaskGroupStateHandler implements StateEventHandler { |
||||
|
||||
@Override |
||||
public boolean handleStateEvent(WorkflowExecuteRunnable workflowExecuteRunnable, |
||||
StateEvent stateEvent) { |
||||
log.info("Handle task instance wait task group event, taskInstanceId: {}", stateEvent.getTaskInstanceId()); |
||||
if (workflowExecuteRunnable.checkForceStartAndWakeUp(stateEvent)) { |
||||
log.info("Success wake up task instance, taskInstanceId: {}", stateEvent.getTaskInstanceId()); |
||||
} else { |
||||
log.info("Failed to wake up task instance, taskInstanceId: {}", stateEvent.getTaskInstanceId()); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public StateEventType getEventType() { |
||||
return StateEventType.WAKE_UP_TASK_GROUP; |
||||
} |
||||
} |
@ -1,58 +0,0 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.rpc; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.StateEventType; |
||||
import org.apache.dolphinscheduler.extract.master.transportor.TaskInstanceForceStartRequest; |
||||
import org.apache.dolphinscheduler.extract.master.transportor.TaskInstanceForceStartResponse; |
||||
import org.apache.dolphinscheduler.plugin.task.api.utils.LogUtils; |
||||
import org.apache.dolphinscheduler.server.master.event.TaskStateEvent; |
||||
import org.apache.dolphinscheduler.server.master.processor.queue.StateEventResponseService; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
@Slf4j |
||||
@Component |
||||
public class TaskInstanceForceStartOperationFunction |
||||
implements |
||||
ITaskInstanceOperationFunction<TaskInstanceForceStartRequest, TaskInstanceForceStartResponse> { |
||||
|
||||
@Autowired |
||||
private StateEventResponseService stateEventResponseService; |
||||
|
||||
@Override |
||||
public TaskInstanceForceStartResponse operate(TaskInstanceForceStartRequest taskInstanceForceStartRequest) { |
||||
TaskStateEvent stateEvent = TaskStateEvent.builder() |
||||
.processInstanceId(taskInstanceForceStartRequest.getProcessInstanceId()) |
||||
.taskInstanceId(taskInstanceForceStartRequest.getTaskInstanceId()) |
||||
.key(taskInstanceForceStartRequest.getKey()) |
||||
.type(StateEventType.WAKE_UP_TASK_GROUP) |
||||
.build(); |
||||
try { |
||||
LogUtils.setWorkflowAndTaskInstanceIDMDC(stateEvent.getProcessInstanceId(), stateEvent.getTaskInstanceId()); |
||||
log.info("Received forceStartTaskInstance, event: {}", stateEvent); |
||||
stateEventResponseService.addEvent2WorkflowExecute(stateEvent); |
||||
return TaskInstanceForceStartResponse.success(); |
||||
} finally { |
||||
LogUtils.removeWorkflowAndTaskInstanceIdMDC(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,451 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.runner.taskgroup; |
||||
|
||||
import org.apache.dolphinscheduler.common.constants.Constants; |
||||
import org.apache.dolphinscheduler.common.enums.Flag; |
||||
import org.apache.dolphinscheduler.common.enums.TaskGroupQueueStatus; |
||||
import org.apache.dolphinscheduler.common.enums.WorkflowExecutionStatus; |
||||
import org.apache.dolphinscheduler.common.lifecycle.ServerLifeCycleManager; |
||||
import org.apache.dolphinscheduler.common.thread.BaseDaemonThread; |
||||
import org.apache.dolphinscheduler.common.thread.ThreadUtils; |
||||
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroup; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||
import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupQueueDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; |
||||
import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory; |
||||
import org.apache.dolphinscheduler.extract.master.IWorkflowInstanceService; |
||||
import org.apache.dolphinscheduler.extract.master.transportor.TaskInstanceWakeupRequest; |
||||
import org.apache.dolphinscheduler.extract.master.transportor.TaskInstanceWakeupResponse; |
||||
import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; |
||||
import org.apache.dolphinscheduler.plugin.task.api.utils.LogUtils; |
||||
import org.apache.dolphinscheduler.registry.api.RegistryClient; |
||||
import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; |
||||
|
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
import org.apache.commons.lang3.time.StopWatch; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.function.Function; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* The TaskGroupCoordinator use to manage the task group slot. The task group slot is used to limit the number of {@link TaskInstance} that can be run at the same time. |
||||
* <p> |
||||
* The {@link TaskGroupQueue} is used to represent the task group slot. When a {@link TaskGroupQueue} which inQueue is YES means the {@link TaskGroupQueue} is using by a {@link TaskInstance}. |
||||
* <p> |
||||
* When the {@link TaskInstance} need to use task group, we should use @{@link TaskGroupCoordinator#acquireTaskGroupSlot(TaskInstance)} to acquire the task group slot, |
||||
* this method doesn't block should always acquire successfully, and you should directly stop dispatch the task instance. |
||||
* When the task group slot is available, the TaskGroupCoordinator will wake up the waiting {@link TaskInstance} to dispatch. |
||||
* <pre> |
||||
* if(needAcquireTaskGroupSlot(taskInstance)) { |
||||
* taskGroupCoordinator.acquireTaskGroupSlot(taskInstance); |
||||
* return; |
||||
* } |
||||
* </pre> |
||||
* <p> |
||||
* When the {@link TaskInstance} is finished, we should use @{@link TaskGroupCoordinator#releaseTaskGroupSlot(TaskInstance)} to release the task group slot. |
||||
* <pre> |
||||
* if(needToReleaseTaskGroupSlot(taskInstance)) { |
||||
* taskGroupCoordinator.releaseTaskGroupSlot(taskInstance); |
||||
* } |
||||
* </pre> |
||||
*/ |
||||
@Slf4j |
||||
@Component |
||||
public class TaskGroupCoordinator extends BaseDaemonThread { |
||||
|
||||
@Autowired |
||||
private RegistryClient registryClient; |
||||
|
||||
@Autowired |
||||
private TaskGroupDao taskGroupDao; |
||||
|
||||
@Autowired |
||||
private TaskGroupQueueDao taskGroupQueueDao; |
||||
|
||||
@Autowired |
||||
private TaskInstanceDao taskInstanceDao; |
||||
|
||||
@Autowired |
||||
private ProcessInstanceDao processInstanceDao; |
||||
|
||||
public TaskGroupCoordinator() { |
||||
super("TaskGroupCoordinator"); |
||||
} |
||||
|
||||
@Override |
||||
public synchronized void start() { |
||||
log.info("TaskGroupCoordinator starting..."); |
||||
super.start(); |
||||
log.info("TaskGroupCoordinator started..."); |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
while (!ServerLifeCycleManager.isStopped()) { |
||||
try { |
||||
if (!ServerLifeCycleManager.isRunning()) { |
||||
continue; |
||||
} |
||||
try { |
||||
registryClient.getLock(RegistryNodeType.MASTER_TASK_GROUP_COORDINATOR_LOCK.getRegistryPath()); |
||||
StopWatch taskGroupCoordinatorRoundTimeCost = StopWatch.createStarted(); |
||||
|
||||
amendTaskGroupUseSize(); |
||||
amendTaskGroupQueueStatus(); |
||||
dealWithForceStartTaskGroupQueue(); |
||||
dealWithWaitingTaskGroupQueue(); |
||||
|
||||
taskGroupCoordinatorRoundTimeCost.stop(); |
||||
log.info("TaskGroupCoordinator round time cost: {}/ms", |
||||
taskGroupCoordinatorRoundTimeCost.getTime()); |
||||
} finally { |
||||
registryClient.releaseLock(RegistryNodeType.MASTER_TASK_GROUP_COORDINATOR_LOCK.getRegistryPath()); |
||||
} |
||||
} catch (Throwable e) { |
||||
log.error("TaskGroupCoordinator error", e); |
||||
} finally { |
||||
// sleep 5s
|
||||
ThreadUtils.sleep(Constants.SLEEP_TIME_MILLIS * 5); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Make sure the TaskGroup useSize is equal to the TaskGroupQueue which status is {@link TaskGroupQueueStatus#ACQUIRE_SUCCESS} and forceStart is {@link org.apache.dolphinscheduler.common.enums.Flag#NO}. |
||||
*/ |
||||
private void amendTaskGroupUseSize() { |
||||
// The TaskGroup useSize should equal to the TaskGroupQueue which inQueue is YES and forceStart is NO
|
||||
List<TaskGroup> taskGroups = taskGroupDao.queryAllTaskGroups(); |
||||
if (CollectionUtils.isEmpty(taskGroups)) { |
||||
return; |
||||
} |
||||
for (TaskGroup taskGroup : taskGroups) { |
||||
List<TaskGroupQueue> taskGroupQueues = |
||||
taskGroupQueueDao.queryAcquiredTaskGroupQueueByGroupId(taskGroup.getId()); |
||||
int actualUseSize = taskGroupQueues.size(); |
||||
if (taskGroup.getUseSize() == actualUseSize) { |
||||
continue; |
||||
} |
||||
log.warn("The TaskGroup: {} useSize is {}, but the actual use size is {}, will amend it", |
||||
taskGroup.getName(), |
||||
taskGroup.getUseSize(), actualUseSize); |
||||
taskGroup.setUseSize(actualUseSize); |
||||
taskGroupDao.updateById(taskGroup); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Make sure the TaskGroupQueue status is {@link TaskGroupQueueStatus#RELEASE} when the related {@link TaskInstance} is not exist or status is finished. |
||||
*/ |
||||
private void amendTaskGroupQueueStatus() { |
||||
List<TaskGroupQueue> taskGroupQueues = taskGroupQueueDao.queryAllInQueueTaskGroupQueue(); |
||||
List<Integer> taskInstanceIds = taskGroupQueues.stream() |
||||
.map(TaskGroupQueue::getTaskId) |
||||
.collect(Collectors.toList()); |
||||
Map<Integer, TaskInstance> taskInstanceMap = taskInstanceDao.queryByIds(taskInstanceIds) |
||||
.stream() |
||||
.collect(Collectors.toMap(TaskInstance::getId, Function.identity())); |
||||
|
||||
for (TaskGroupQueue taskGroupQueue : taskGroupQueues) { |
||||
int taskId = taskGroupQueue.getTaskId(); |
||||
TaskInstance taskInstance = taskInstanceMap.get(taskId); |
||||
|
||||
if (taskInstance == null) { |
||||
log.warn("The TaskInstance: {} is not exist, will release the TaskGroupQueue: {}", taskId, |
||||
taskGroupQueue); |
||||
releaseTaskGroupQueueSlot(taskGroupQueue); |
||||
continue; |
||||
} |
||||
|
||||
if (taskInstance.getState().isFinished()) { |
||||
log.warn("The TaskInstance: {} state: {} finished, will release the TaskGroupQueue: {}", |
||||
taskInstance.getName(), taskInstance.getState(), taskGroupQueue); |
||||
releaseTaskGroupQueueSlot(taskGroupQueue); |
||||
continue; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void dealWithForceStartTaskGroupQueue() { |
||||
// Find the force start task group queue(Which is inQueue and forceStart is YES)
|
||||
// Notify the related waiting task instance
|
||||
// Set the taskGroupQueue status to RELEASE and remove it from queue
|
||||
List<TaskGroupQueue> taskGroupQueues = taskGroupQueueDao.queryAllInQueueTaskGroupQueue() |
||||
.stream() |
||||
.filter(taskGroupQueue -> Flag.YES.getCode() == taskGroupQueue.getForceStart()) |
||||
.collect(Collectors.toList()); |
||||
for (TaskGroupQueue taskGroupQueue : taskGroupQueues) { |
||||
try { |
||||
LogUtils.setTaskInstanceIdMDC(taskGroupQueue.getTaskId()); |
||||
// notify the waiting task instance
|
||||
// We notify first, it notify failed, the taskGroupQueue will be in queue, and then we will retry it
|
||||
// next time.
|
||||
notifyWaitingTaskInstance(taskGroupQueue); |
||||
log.info("Notify the ForceStart waiting TaskInstance: {} for taskGroupQueue: {} success", |
||||
taskGroupQueue.getTaskName(), |
||||
taskGroupQueue.getId()); |
||||
|
||||
taskGroupQueue.setInQueue(Flag.NO.getCode()); |
||||
taskGroupQueue.setStatus(TaskGroupQueueStatus.RELEASE); |
||||
taskGroupQueue.setUpdateTime(new Date()); |
||||
taskGroupQueueDao.updateById(taskGroupQueue); |
||||
log.info("Release the force start TaskGroupQueue {}", taskGroupQueue); |
||||
} catch (UnsupportedOperationException unsupportedOperationException) { |
||||
releaseTaskGroupQueueSlot(taskGroupQueue); |
||||
log.info( |
||||
"Notify the ForceStart TaskInstance: {} for taskGroupQueue: {} failed, will release the taskGroupQueue", |
||||
taskGroupQueue.getTaskName(), taskGroupQueue.getId(), unsupportedOperationException); |
||||
} catch (Throwable throwable) { |
||||
log.info("Notify the force start TaskGroupQueue {} failed", taskGroupQueue, throwable); |
||||
} finally { |
||||
LogUtils.removeTaskInstanceIdMDC(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void dealWithWaitingTaskGroupQueue() { |
||||
// Find the TaskGroup which usage < maxSize.
|
||||
// Find the highest priority inQueue task group queue(Which is inQueue and status is Waiting and force start is
|
||||
// NO) belong to the
|
||||
// task group.
|
||||
List<TaskGroup> taskGroups = taskGroupDao.queryAvailableTaskGroups(); |
||||
if (CollectionUtils.isEmpty(taskGroups)) { |
||||
log.debug("There is no available task group"); |
||||
return; |
||||
} |
||||
for (TaskGroup taskGroup : taskGroups) { |
||||
int availableSize = taskGroup.getGroupSize() - taskGroup.getUseSize(); |
||||
if (availableSize <= 0) { |
||||
log.info("TaskGroup {} is full, available size is {}", taskGroup, availableSize); |
||||
continue; |
||||
} |
||||
List<TaskGroupQueue> taskGroupQueues = |
||||
taskGroupQueueDao.queryAllInQueueTaskGroupQueueByGroupId(taskGroup.getId()) |
||||
.stream() |
||||
.filter(taskGroupQueue -> Flag.NO.getCode() == taskGroupQueue.getForceStart()) |
||||
.filter(taskGroupQueue -> TaskGroupQueueStatus.WAIT_QUEUE == taskGroupQueue.getStatus()) |
||||
.limit(availableSize) |
||||
.collect(Collectors.toList()); |
||||
if (CollectionUtils.isEmpty(taskGroupQueues)) { |
||||
log.debug("There is no waiting task group queue for task group {}", taskGroup.getName()); |
||||
continue; |
||||
} |
||||
for (TaskGroupQueue taskGroupQueue : taskGroupQueues) { |
||||
try { |
||||
LogUtils.setTaskInstanceIdMDC(taskGroupQueue.getTaskId()); |
||||
// Reduce the taskGroupSize
|
||||
boolean acquireResult = taskGroupDao.acquireTaskGroupSlot(taskGroup.getId()); |
||||
if (!acquireResult) { |
||||
log.error("Failed to acquire task group slot for task group {}", taskGroup); |
||||
continue; |
||||
} |
||||
// Notify the waiting task instance
|
||||
// We notify first, it notify failed, the taskGroupQueue will be in queue, and then we will retry it
|
||||
// next time.
|
||||
notifyWaitingTaskInstance(taskGroupQueue); |
||||
|
||||
// Set the taskGroupQueue status to RUNNING and remove from queue
|
||||
taskGroupQueue.setInQueue(Flag.YES.getCode()); |
||||
taskGroupQueue.setStatus(TaskGroupQueueStatus.ACQUIRE_SUCCESS); |
||||
taskGroupQueue.setUpdateTime(new Date()); |
||||
taskGroupQueueDao.updateById(taskGroupQueue); |
||||
} catch (UnsupportedOperationException unsupportedOperationException) { |
||||
releaseTaskGroupQueueSlot(taskGroupQueue); |
||||
log.info( |
||||
"Notify the Waiting TaskInstance: {} for taskGroupQueue: {} failed, will release the taskGroupQueue", |
||||
taskGroupQueue.getTaskName(), taskGroupQueue.getId(), unsupportedOperationException); |
||||
} catch (Throwable throwable) { |
||||
log.error("Notify Waiting TaskGroupQueue: {} failed", taskGroupQueue, throwable); |
||||
} finally { |
||||
LogUtils.removeTaskInstanceIdMDC(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* If the {@link TaskInstance#getTaskGroupId()} > 0, and the TaskGroup flag is {@link Flag#YES} then the task instance need to use task group. |
||||
* |
||||
* @param taskInstance task instance |
||||
* @return true if the TaskInstance need to acquireTaskGroupSlot |
||||
*/ |
||||
public boolean needAcquireTaskGroupSlot(TaskInstance taskInstance) { |
||||
if (taskInstance == null) { |
||||
throw new IllegalArgumentException("The TaskInstance is null"); |
||||
} |
||||
if (taskInstance.getTaskGroupId() <= 0) { |
||||
log.debug("The current TaskInstance doesn't use TaskGroup, no need to acquire TaskGroupSlot"); |
||||
return false; |
||||
} |
||||
TaskGroup taskGroup = taskGroupDao.queryById(taskInstance.getTaskGroupId()); |
||||
if (taskGroup == null) { |
||||
log.warn("The current TaskGroup: {} does not exist, will not acquire TaskGroupSlot", |
||||
taskInstance.getTaskGroupId()); |
||||
return false; |
||||
} |
||||
return Flag.YES.equals(taskGroup.getStatus()); |
||||
} |
||||
|
||||
/** |
||||
* Acquire the task group slot for the given {@link TaskInstance}. |
||||
* <p> |
||||
* When taskInstance want to acquire a TaskGroup slot, should call this method. If acquire successfully, will create a TaskGroupQueue in db which is in queue and status is {@link TaskGroupQueueStatus#WAIT_QUEUE}. |
||||
* The TaskInstance shouldn't dispatch until there exist available slot, the taskGroupCoordinator notify it. |
||||
* |
||||
* @param taskInstance the task instance which want to acquire task group slot. |
||||
* @throws IllegalArgumentException if the taskInstance is null or the used taskGroup doesn't exist. |
||||
*/ |
||||
public void acquireTaskGroupSlot(TaskInstance taskInstance) { |
||||
if (taskInstance == null || taskInstance.getTaskGroupId() <= 0) { |
||||
throw new IllegalArgumentException("The current TaskInstance does not use task group"); |
||||
} |
||||
TaskGroup taskGroup = taskGroupDao.queryById(taskInstance.getTaskGroupId()); |
||||
if (taskGroup == null) { |
||||
throw new IllegalArgumentException( |
||||
"The current TaskGroup: " + taskInstance.getTaskGroupId() + " does not exist"); |
||||
} |
||||
// Write TaskGroupQueue in db, and then return wait TaskGroupCoordinator to notify it
|
||||
// Set the taskGroupQueue status to WAIT_QUEUE and add to queue
|
||||
// The queue only contains the taskGroupQueue which status is WAIT_QUEUE or ACQUIRE_SUCCESS
|
||||
Date now = new Date(); |
||||
TaskGroupQueue taskGroupQueue = TaskGroupQueue |
||||
.builder() |
||||
.taskId(taskInstance.getId()) |
||||
.taskName(taskInstance.getName()) |
||||
.groupId(taskInstance.getTaskGroupId()) |
||||
.processId(taskInstance.getProcessInstanceId()) |
||||
.priority(taskInstance.getTaskGroupPriority()) |
||||
.inQueue(Flag.YES.getCode()) |
||||
.forceStart(Flag.NO.getCode()) |
||||
.status(TaskGroupQueueStatus.WAIT_QUEUE) |
||||
.createTime(now) |
||||
.updateTime(now) |
||||
.build(); |
||||
log.info("Success insert TaskGroupQueue: {} for TaskInstance: {}", taskGroupQueue, taskInstance.getName()); |
||||
taskGroupQueueDao.insert(taskGroupQueue); |
||||
} |
||||
|
||||
/** |
||||
* If the TaskInstance is using TaskGroup then it need to release TaskGroupSlot. |
||||
* |
||||
* @param taskInstance taskInsatnce |
||||
* @return true if the TaskInstance need to release TaskGroupSlot |
||||
*/ |
||||
public boolean needToReleaseTaskGroupSlot(TaskInstance taskInstance) { |
||||
if (taskInstance == null) { |
||||
throw new IllegalArgumentException("The TaskInstance is null"); |
||||
} |
||||
if (taskInstance.getTaskGroupId() <= 0) { |
||||
log.debug("The current TaskInstance doesn't use TaskGroup, no need to release TaskGroupSlot"); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Release the task group slot for the given {@link TaskInstance}. |
||||
* <p> |
||||
* When taskInstance want to release a TaskGroup slot, should call this method. The release method will move the TaskGroupQueue out queue and set status to {@link TaskGroupQueueStatus#RELEASE}. |
||||
* This method is idempotent, this means that if the task group slot is already released, this method will do nothing. |
||||
* |
||||
* @param taskInstance the task instance which want to release task group slot. |
||||
* @throws IllegalArgumentException If the taskInstance is null or the task doesn't use task group. |
||||
*/ |
||||
public void releaseTaskGroupSlot(TaskInstance taskInstance) { |
||||
if (taskInstance == null || taskInstance.getTaskGroupId() <= 0) { |
||||
throw new IllegalArgumentException("The current TaskInstance does not use task group"); |
||||
} |
||||
List<TaskGroupQueue> taskGroupQueues = taskGroupQueueDao.queryByTaskInstanceId(taskInstance.getId()); |
||||
for (TaskGroupQueue taskGroupQueue : taskGroupQueues) { |
||||
releaseTaskGroupQueueSlot(taskGroupQueue); |
||||
} |
||||
} |
||||
|
||||
private void notifyWaitingTaskInstance(TaskGroupQueue taskGroupQueue) { |
||||
// Find the related waiting task instance
|
||||
// send RPC to notify the waiting task instance
|
||||
TaskInstance taskInstance = taskInstanceDao.queryById(taskGroupQueue.getTaskId()); |
||||
if (taskInstance == null) { |
||||
throw new UnsupportedOperationException( |
||||
"The TaskInstance: " + taskGroupQueue.getTaskId() + " is not exist, no need to notify"); |
||||
} |
||||
// todo: We may need to add a new status to represent the task instance is waiting for task group slot
|
||||
if (taskInstance.getState() != TaskExecutionStatus.SUBMITTED_SUCCESS) { |
||||
throw new UnsupportedOperationException( |
||||
"The TaskInstance: " + taskInstance.getId() + " state is " + taskInstance.getState() |
||||
+ ", no need to notify"); |
||||
} |
||||
ProcessInstance processInstance = processInstanceDao.queryById(taskInstance.getProcessInstanceId()); |
||||
if (processInstance == null) { |
||||
throw new UnsupportedOperationException( |
||||
"The WorkflowInstance: " + taskInstance.getProcessInstanceId() |
||||
+ " is not exist, no need to notify"); |
||||
} |
||||
if (processInstance.getState() != WorkflowExecutionStatus.RUNNING_EXECUTION) { |
||||
throw new UnsupportedOperationException( |
||||
"The WorkflowInstance: " + processInstance.getId() + " state is " + processInstance.getState() |
||||
+ ", no need to notify"); |
||||
} |
||||
if (processInstance.getHost() == null || Constants.NULL.equals(processInstance.getHost())) { |
||||
throw new UnsupportedOperationException( |
||||
"WorkflowInstance host is null, maybe it is in failover: " + processInstance); |
||||
} |
||||
|
||||
TaskInstanceWakeupRequest taskInstanceWakeupRequest = TaskInstanceWakeupRequest.builder() |
||||
.processInstanceId(processInstance.getId()) |
||||
.taskInstanceId(taskInstance.getId()) |
||||
.build(); |
||||
|
||||
IWorkflowInstanceService iWorkflowInstanceService = SingletonJdkDynamicRpcClientProxyFactory |
||||
.getProxyClient(processInstance.getHost(), IWorkflowInstanceService.class); |
||||
TaskInstanceWakeupResponse taskInstanceWakeupResponse = |
||||
iWorkflowInstanceService.wakeupTaskInstance(taskInstanceWakeupRequest); |
||||
if (!taskInstanceWakeupResponse.isSuccess()) { |
||||
throw new UnsupportedOperationException( |
||||
"Notify TaskInstance: " + taskInstance.getId() + " failed: " + taskInstanceWakeupResponse); |
||||
} |
||||
log.info("Wake up TaskInstance: {} success", taskInstance.getName()); |
||||
} |
||||
|
||||
private void releaseTaskGroupQueueSlot(TaskGroupQueue taskGroupQueue) { |
||||
if (TaskGroupQueueStatus.RELEASE.equals(taskGroupQueue.getStatus()) |
||||
&& Flag.NO.getCode() == taskGroupQueue.getInQueue()) { |
||||
log.info("The TaskGroupQueue: {} is already released", taskGroupQueue); |
||||
return; |
||||
} |
||||
taskGroupQueue.setInQueue(Flag.NO.getCode()); |
||||
taskGroupQueue.setStatus(TaskGroupQueueStatus.RELEASE); |
||||
taskGroupQueue.setUpdateTime(new Date()); |
||||
taskGroupQueueDao.updateById(taskGroupQueue); |
||||
log.info("Success release TaskGroupQueue: {}", taskGroupQueue); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,182 @@
|
||||
/* |
||||
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||
* contributor license agreements. See the NOTICE file distributed with |
||||
* this work for additional information regarding copyright ownership. |
||||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.apache.dolphinscheduler.server.master.runner.taskgroup; |
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
import static org.junit.jupiter.api.Assertions.assertFalse; |
||||
import static org.junit.jupiter.api.Assertions.assertThrows; |
||||
import static org.junit.jupiter.api.Assertions.assertTrue; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.Flag; |
||||
import org.apache.dolphinscheduler.common.enums.TaskGroupQueueStatus; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroup; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue; |
||||
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||
import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskGroupQueueDao; |
||||
import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; |
||||
import org.apache.dolphinscheduler.registry.api.RegistryClient; |
||||
import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Assertions; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.jupiter.MockitoExtension; |
||||
import org.mockito.junit.jupiter.MockitoSettings; |
||||
import org.mockito.quality.Strictness; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
|
||||
@ExtendWith(MockitoExtension.class) |
||||
@MockitoSettings(strictness = Strictness.LENIENT) |
||||
class TaskGroupCoordinatorTest { |
||||
|
||||
@InjectMocks |
||||
private TaskGroupCoordinator taskGroupCoordinator; |
||||
|
||||
@Mock |
||||
private RegistryClient registryClient; |
||||
|
||||
@Mock |
||||
private TaskGroupDao taskGroupDao; |
||||
|
||||
@Mock |
||||
private TaskGroupQueueDao taskGroupQueueDao; |
||||
|
||||
@Mock |
||||
private TaskInstanceDao taskInstanceDao; |
||||
|
||||
@Mock |
||||
private ProcessInstanceDao processInstanceDao; |
||||
|
||||
@Test |
||||
void start() throws InterruptedException { |
||||
// Get the Lock from Registry
|
||||
taskGroupCoordinator.start(); |
||||
Thread.sleep(1_000); |
||||
verify(registryClient, Mockito.times(1)) |
||||
.getLock(RegistryNodeType.MASTER_TASK_GROUP_COORDINATOR_LOCK.getRegistryPath()); |
||||
verify(registryClient, Mockito.times(1)) |
||||
.releaseLock(RegistryNodeType.MASTER_TASK_GROUP_COORDINATOR_LOCK.getRegistryPath()); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
void needAcquireTaskGroupSlot() { |
||||
// TaskInstance is null
|
||||
IllegalArgumentException illegalArgumentException = |
||||
assertThrows(IllegalArgumentException.class, () -> taskGroupCoordinator.needAcquireTaskGroupSlot(null)); |
||||
assertEquals("The TaskInstance is null", illegalArgumentException.getMessage()); |
||||
|
||||
// TaskGroupId < 0
|
||||
TaskInstance taskInstance = new TaskInstance(); |
||||
assertFalse(taskGroupCoordinator.needAcquireTaskGroupSlot(taskInstance)); |
||||
|
||||
// TaskGroup not exist
|
||||
taskInstance.setTaskGroupId(1); |
||||
when(taskGroupDao.queryById(taskInstance.getTaskGroupId())).thenReturn(null); |
||||
assertFalse(taskGroupCoordinator.needAcquireTaskGroupSlot(taskInstance)); |
||||
|
||||
// TaskGroup is closed
|
||||
TaskGroup taskGroup = new TaskGroup(); |
||||
taskGroup.setStatus(Flag.NO); |
||||
when(taskGroupDao.queryById(taskInstance.getTaskGroupId())).thenReturn(taskGroup); |
||||
assertFalse(taskGroupCoordinator.needAcquireTaskGroupSlot(taskInstance)); |
||||
|
||||
// TaskGroup is open
|
||||
taskGroup.setStatus(Flag.YES); |
||||
when(taskGroupDao.queryById(taskInstance.getTaskGroupId())).thenReturn(taskGroup); |
||||
assertTrue(taskGroupCoordinator.needToReleaseTaskGroupSlot(taskInstance)); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
void acquireTaskGroupSlot() { |
||||
// TaskInstance is NULL
|
||||
IllegalArgumentException illegalArgumentException = |
||||
assertThrows(IllegalArgumentException.class, () -> taskGroupCoordinator.acquireTaskGroupSlot(null)); |
||||
assertEquals("The current TaskInstance does not use task group", illegalArgumentException.getMessage()); |
||||
|
||||
// TaskGroupId is NULL
|
||||
TaskInstance taskInstance = new TaskInstance(); |
||||
illegalArgumentException = assertThrows(IllegalArgumentException.class, |
||||
() -> taskGroupCoordinator.acquireTaskGroupSlot(taskInstance)); |
||||
assertEquals("The current TaskInstance does not use task group", illegalArgumentException.getMessage()); |
||||
|
||||
// TaskGroup not exist
|
||||
taskInstance.setTaskGroupId(1); |
||||
taskInstance.setId(1); |
||||
when(taskGroupDao.queryById(taskInstance.getTaskGroupId())).thenReturn(null); |
||||
illegalArgumentException = assertThrows(IllegalArgumentException.class, |
||||
() -> taskGroupCoordinator.acquireTaskGroupSlot(taskInstance)); |
||||
assertEquals("The current TaskGroup: 1 does not exist", illegalArgumentException.getMessage()); |
||||
|
||||
// TaskGroup exist
|
||||
when(taskGroupDao.queryById(taskInstance.getTaskGroupId())).thenReturn(new TaskGroup()); |
||||
Assertions.assertDoesNotThrow(() -> taskGroupCoordinator.acquireTaskGroupSlot(taskInstance)); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
void needToReleaseTaskGroupSlot() { |
||||
IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, |
||||
() -> taskGroupCoordinator.needToReleaseTaskGroupSlot(null)); |
||||
assertEquals("The TaskInstance is null", illegalArgumentException.getMessage()); |
||||
|
||||
TaskInstance taskInstance = new TaskInstance(); |
||||
assertFalse(taskGroupCoordinator.needToReleaseTaskGroupSlot(taskInstance)); |
||||
|
||||
taskInstance.setTaskGroupId(1); |
||||
assertTrue(taskGroupCoordinator.needToReleaseTaskGroupSlot(taskInstance)); |
||||
} |
||||
|
||||
@Test |
||||
void releaseTaskGroupSlot() { |
||||
// TaskInstance is NULL
|
||||
IllegalArgumentException illegalArgumentException = |
||||
assertThrows(IllegalArgumentException.class, () -> taskGroupCoordinator.releaseTaskGroupSlot(null)); |
||||
assertEquals("The current TaskInstance does not use task group", illegalArgumentException.getMessage()); |
||||
|
||||
// TaskGroupId is NULL
|
||||
TaskInstance taskInstance = new TaskInstance(); |
||||
illegalArgumentException = assertThrows(IllegalArgumentException.class, |
||||
() -> taskGroupCoordinator.releaseTaskGroupSlot(taskInstance)); |
||||
assertEquals("The current TaskInstance does not use task group", illegalArgumentException.getMessage()); |
||||
|
||||
// Release TaskGroupQueue
|
||||
taskInstance.setId(1); |
||||
taskInstance.setTaskGroupId(1); |
||||
TaskGroupQueue taskGroupQueue = new TaskGroupQueue(); |
||||
List<TaskGroupQueue> taskGroupQueues = Lists.newArrayList(taskGroupQueue); |
||||
when(taskGroupQueueDao.queryByTaskInstanceId(taskInstance.getId())).thenReturn(taskGroupQueues); |
||||
taskGroupCoordinator.releaseTaskGroupSlot(taskInstance); |
||||
|
||||
assertEquals(Flag.NO.getCode(), taskGroupQueue.getInQueue()); |
||||
assertEquals(TaskGroupQueueStatus.RELEASE, taskGroupQueue.getStatus()); |
||||
verify(taskGroupQueueDao, Mockito.times(1)).updateById(taskGroupQueue); |
||||
|
||||
} |
||||
} |
Loading…
Reference in new issue