001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.monitoring;
019
020import java.util.Collection;
021import java.util.Collections;
022import java.util.concurrent.ConcurrentLinkedDeque;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * The {@link TaskGroup} can be seen as a big {@link MonitoredTask}, which contains a list of sub
029 * monitored tasks. The monitored tasks in the group are still be managed by the
030 * {@link TaskMonitor}, but whether to clear/expire the monitored tasks in a task group is optional.
031 * Since the monitored task already has journals, which mark the phases in a task, we still also
032 * need a task group to monitor a big task/process because the journals in a task is serial but the
033 * tasks in the task group can be parallel, then we have more flexible ability to monitor the
034 * process. Grouping the tasks is not strictly necessary but it is cleaner for presentation to
035 * operators. We might want to display the tasks in a group in a list view where each task can be
036 * collapsed (probably by default) or expanded.
037 */
038@InterfaceAudience.Private
039public class TaskGroup extends MonitoredTaskImpl {
040  private static final Logger LOG = LoggerFactory.getLogger(TaskGroup.class);
041
042  /** Sub-tasks in the group */
043  private final ConcurrentLinkedDeque<MonitoredTask> tasks = new ConcurrentLinkedDeque<>();
044
045  /** Whether to ignore to track(e.g. show/clear/expire) in the singleton {@link TaskMonitor} */
046  private boolean ignoreSubTasksInTaskMonitor;
047
048  /** Used to track this task group in {@link TaskMonitor} */
049  private final MonitoredTask delegate;
050
051  public TaskGroup(boolean ignoreSubTasksInTaskMonitor, String description) {
052    super(false, description);
053    this.ignoreSubTasksInTaskMonitor = ignoreSubTasksInTaskMonitor;
054    this.delegate = TaskMonitor.get().createStatus(description, false, true);
055  }
056
057  public synchronized MonitoredTask addTask(String description) {
058    return addTask(description, true);
059  }
060
061  /**
062   * Add a new task to the group, and before that might complete the last task in the group
063   * @param description      the description of the new task
064   * @param withCompleteLast whether to complete the last task in the group
065   * @return the added new task
066   */
067  public synchronized MonitoredTask addTask(String description, boolean withCompleteLast) {
068    if (withCompleteLast) {
069      MonitoredTask previousTask = this.tasks.peekLast();
070      if (
071        previousTask != null && previousTask.getState() != State.COMPLETE
072          && previousTask.getState() != State.ABORTED
073      ) {
074        previousTask.markComplete("Completed");
075      }
076    }
077    MonitoredTask task =
078      TaskMonitor.get().createStatus(description, ignoreSubTasksInTaskMonitor, true);
079    this.setStatus(description);
080    this.tasks.addLast(task);
081    delegate.setStatus(description);
082    return task;
083  }
084
085  public synchronized Collection<MonitoredTask> getTasks() {
086    return Collections.unmodifiableCollection(this.tasks);
087  }
088
089  @Override
090  public synchronized void abort(String msg) {
091    setStatus(msg);
092    setState(State.ABORTED);
093    for (MonitoredTask task : tasks) {
094      if (task.getState() != State.COMPLETE && task.getState() != State.ABORTED) {
095        task.abort(msg);
096      }
097    }
098    delegate.abort(msg);
099  }
100
101  @Override
102  public synchronized void markComplete(String msg) {
103    setState(State.COMPLETE);
104    setStatus(msg);
105    if (tasks.getLast() != null) {
106      tasks.getLast().markComplete(msg);
107    }
108    delegate.markComplete(msg);
109  }
110
111  @Override
112  public synchronized void cleanup() {
113    this.tasks.clear();
114  }
115}