@@ -464,9 +464,23 @@ public SubmitResult executeTaskAsync(Task task, ExecuteOption option) { |
464 | 464 |
|
465 | 465 | /** |
466 | 466 | * Suspend a task, which will stop the task scheduler and kill the running task run. |
467 | | - * NOTE: this method is thread safe, no need to task lock again. |
| 467 | + * NOTE: this method will write edit log. |
468 | 468 | */ |
469 | 469 | public void suspendTask(Task task) { |
| 470 | + if (task == null) { |
| 471 | + return; |
| 472 | + } |
| 473 | + GlobalStateMgr.getCurrentState().getEditLog().logAlterTask( |
| 474 | + new AlterTaskInfo(task.getName(), Constants.TaskState.PAUSE), |
| 475 | + wal -> task.setState(Constants.TaskState.PAUSE) |
| 476 | + ); |
| 477 | + suspendTaskInternal(task); |
| 478 | + } |
| 479 | + |
| 480 | + /** |
| 481 | + * Internal method to suspend a task without writing edit log. |
| 482 | + */ |
| 483 | + private void suspendTaskInternal(Task task) { |
470 | 484 | if (task == null) { |
471 | 485 | return; |
472 | 486 | } |
@@ -496,9 +510,23 @@ public void suspendTask(Task task) { |
496 | 510 |
|
497 | 511 | /** |
498 | 512 | * Resume a task, which will restart the task scheduler if the task is periodical. |
499 | | - * NOTE: This method is thread safe, no need to task lock again. |
| 513 | + * NOTE: this method will write edit log. |
500 | 514 | */ |
501 | 515 | public void resumeTask(Task task) { |
| 516 | + if (task == null) { |
| 517 | + return; |
| 518 | + } |
| 519 | + GlobalStateMgr.getCurrentState().getEditLog().logAlterTask( |
| 520 | + new AlterTaskInfo(task.getName(), Constants.TaskState.ACTIVE), |
| 521 | + wal -> task.setState(Constants.TaskState.ACTIVE) |
| 522 | + ); |
| 523 | + resumeTaskInternal(task); |
| 524 | + } |
| 525 | + |
| 526 | + /** |
| 527 | + * Internal method to resume a task without writing edit log. |
| 528 | + */ |
| 529 | + private void resumeTaskInternal(Task task) { |
502 | 530 | if (task == null) { |
503 | 531 | return; |
504 | 532 | } |
@@ -520,6 +548,50 @@ public void resumeTask(Task task) { |
520 | 548 | } |
521 | 549 | } |
522 | 550 |
|
| 551 | + /** |
| 552 | + * Update task properties for subsequent executions. |
| 553 | + * NOTE: this method will write edit log. |
| 554 | + */ |
| 555 | + public void updateTaskProperties(Task task, Map<String, String> properties) { |
| 556 | + if (task == null || properties == null || properties.isEmpty()) { |
| 557 | + return; |
| 558 | + } |
| 559 | + GlobalStateMgr.getCurrentState().getEditLog().logAlterTask( |
| 560 | + new AlterTaskInfo(task.getName(), properties), |
| 561 | + wal -> { |
| 562 | + Map<String, String> current = task.getProperties(); |
| 563 | + if (current == null) { |
| 564 | + current = Maps.newHashMap(); |
| 565 | + task.setProperties(current); |
| 566 | + } |
| 567 | + current.putAll(properties); |
| 568 | + } |
| 569 | + ); |
| 570 | + updateTaskPropertiesInternal(task, properties); |
| 571 | + } |
| 572 | + |
| 573 | + /** |
| 574 | + * Internal method to update task properties without writing edit log. |
| 575 | + */ |
| 576 | + private void updateTaskPropertiesInternal(Task task, Map<String, String> properties) { |
| 577 | + if (task == null || properties == null || properties.isEmpty()) { |
| 578 | + return; |
| 579 | + } |
| 580 | + if (!tryTaskLock()) { |
| 581 | + throw new RuntimeException("Failed to get task lock when update Task properties[" + task.getName() + "]"); |
| 582 | + } |
| 583 | + try { |
| 584 | + Map<String, String> current = task.getProperties(); |
| 585 | + if (current == null) { |
| 586 | + current = Maps.newHashMap(); |
| 587 | + task.setProperties(current); |
| 588 | + } |
| 589 | + current.putAll(properties); |
| 590 | + } finally { |
| 591 | + taskUnlock(); |
| 592 | + } |
| 593 | + } |
| 594 | + |
523 | 595 | public void dropTasks(List<Long> taskIdList) { |
524 | 596 | takeTaskLock(); |
525 | 597 | try { |
@@ -647,7 +719,23 @@ private void changeTask(Task currentTask, Constants.TaskType changedType, TaskSc |
647 | 719 |
|
648 | 720 | public void replayAlterTask(AlterTaskInfo alterTaskInfo) { |
649 | 721 | Task currentTask = getTask(alterTaskInfo.getName()); |
650 | | - changeTask(currentTask, alterTaskInfo.getType(), alterTaskInfo.getSchedule()); |
| 722 | + // Handle task type change (existing logic) |
| 723 | + if (alterTaskInfo.getType() != null) { |
| 724 | + changeTask(currentTask, alterTaskInfo.getType(), alterTaskInfo.getSchedule()); |
| 725 | + } |
| 726 | + // Handle task state change (suspend/resume) |
| 727 | + if (alterTaskInfo.getState() != null) { |
| 728 | + currentTask.setState(alterTaskInfo.getState()); |
| 729 | + } |
| 730 | + // Handle properties update |
| 731 | + if (alterTaskInfo.getProperties() != null) { |
| 732 | + Map<String, String> current = currentTask.getProperties(); |
| 733 | + if (current == null) { |
| 734 | + current = Maps.newHashMap(); |
| 735 | + currentTask.setProperties(current); |
| 736 | + } |
| 737 | + current.putAll(alterTaskInfo.getProperties()); |
| 738 | + } |
651 | 739 | } |
652 | 740 |
|
653 | 741 | @VisibleForTesting |
@@ -718,12 +806,13 @@ void registerScheduler(Task task) { |
718 | 806 | task.getName(), initialDelay, periodSeconds, taskStartTime, currentDateTime); |
719 | 807 | // set task's next schedule time |
720 | 808 | task.setNextScheduleTime(currentDateTime.plusSeconds(initialDelay).toEpochSecond(ZoneOffset.UTC)); |
721 | | - ExecuteOption option = new ExecuteOption(Constants.TaskRunPriority.LOWEST.value(), true, task.getProperties()); |
722 | 809 | ScheduledFuture<?> future = periodScheduler.scheduleAtFixedRate(() -> { |
723 | 810 | // ensure an execute task will not throw exception |
724 | 811 | try { |
725 | 812 | task.setLastScheduleTime(TimeUtils.getEpochSeconds()); |
726 | 813 | task.setNextScheduleTime(TimeUtils.getEpochSeconds() + periodSeconds); |
| 814 | + ExecuteOption option = new ExecuteOption(Constants.TaskRunPriority.LOWEST.value(), true, |
| 815 | + task.getProperties()); |
727 | 816 | executeTask(task.getName(), option); |
728 | 817 | } catch (Throwable e) { |
729 | 818 | LOG.warn("failed to execute periodical task: {}", task, e); |
|