001 /** 002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved. 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. See accompanying LICENSE file. 014 */ 015 package org.apache.oozie.service; 016 017 import java.util.ArrayList; 018 import java.util.List; 019 020 import org.apache.hadoop.conf.Configuration; 021 import org.apache.oozie.CoordinatorActionBean; 022 import org.apache.oozie.ErrorCode; 023 import org.apache.oozie.WorkflowActionBean; 024 import org.apache.oozie.command.CommandException; 025 import org.apache.oozie.command.coord.CoordActionCheckCommand; 026 import org.apache.oozie.command.coord.CoordActionCheckXCommand; 027 import org.apache.oozie.command.wf.ActionCheckCommand; 028 import org.apache.oozie.command.wf.ActionCheckXCommand; 029 import org.apache.oozie.executor.jpa.CoordActionsRunningGetJPAExecutor; 030 import org.apache.oozie.executor.jpa.JPAExecutorException; 031 import org.apache.oozie.executor.jpa.WorkflowActionsRunningGetJPAExecutor; 032 import org.apache.oozie.util.XCallable; 033 import org.apache.oozie.util.XLog; 034 035 /** 036 * The Action Checker Service queue ActionCheckCommands to check the status of 037 * running actions and CoordActionCheckCommands to check the status of 038 * coordinator actions. The delay between checks on the same action can be 039 * configured. 040 */ 041 public class ActionCheckerService implements Service { 042 043 public static final String CONF_PREFIX = Service.CONF_PREFIX + "ActionCheckerService."; 044 /** 045 * The frequency at which the ActionCheckService will run. 046 */ 047 public static final String CONF_ACTION_CHECK_INTERVAL = CONF_PREFIX + "action.check.interval"; 048 /** 049 * The time, in seconds, between an ActionCheck for the same action. 050 */ 051 public static final String CONF_ACTION_CHECK_DELAY = CONF_PREFIX + "action.check.delay"; 052 053 /** 054 * The number of callables to be queued in a batch. 055 */ 056 public static final String CONF_CALLABLE_BATCH_SIZE = CONF_PREFIX + "callable.batch.size"; 057 058 protected static final String INSTRUMENTATION_GROUP = "actionchecker"; 059 protected static final String INSTR_CHECK_ACTIONS_COUNTER = "checks_wf_actions"; 060 protected static final String INSTR_CHECK_COORD_ACTIONS_COUNTER = "checks_coord_actions"; 061 062 private static boolean useXCommand = true; 063 064 /** 065 * {@link ActionCheckRunnable} is the runnable which is scheduled to run and 066 * queue Action checks. 067 */ 068 static class ActionCheckRunnable implements Runnable { 069 private int actionCheckDelay; 070 private List<XCallable<Void>> callables; 071 private StringBuilder msg = null; 072 073 public ActionCheckRunnable(int actionCheckDelay) { 074 this.actionCheckDelay = actionCheckDelay; 075 } 076 077 public void run() { 078 XLog.Info.get().clear(); 079 XLog LOG = XLog.getLog(getClass()); 080 msg = new StringBuilder(); 081 try { 082 runWFActionCheck(); 083 runCoordActionCheck(); 084 } 085 catch (CommandException ce) { 086 LOG.error("Unable to run action checks, ", ce); 087 } 088 089 LOG.debug("QUEUING [{0}] for potential checking", msg.toString()); 090 if (null != callables) { 091 boolean ret = Services.get().get(CallableQueueService.class).queueSerial(callables); 092 if (ret == false) { 093 LOG.warn("Unable to queue the callables commands for CheckerService. " 094 + "Most possibly command queue is full. Queue size is :" 095 + Services.get().get(CallableQueueService.class).queueSize()); 096 } 097 callables = null; 098 } 099 } 100 101 /** 102 * check workflow actions 103 * 104 * @throws CommandException 105 */ 106 private void runWFActionCheck() throws CommandException { 107 JPAService jpaService = Services.get().get(JPAService.class); 108 if (jpaService == null) { 109 throw new CommandException(ErrorCode.E0610); 110 } 111 112 List<WorkflowActionBean> actions; 113 try { 114 actions = jpaService 115 .execute(new WorkflowActionsRunningGetJPAExecutor(actionCheckDelay)); 116 } 117 catch (JPAExecutorException je) { 118 throw new CommandException(je); 119 } 120 121 if (actions == null || actions.size() == 0) { 122 return; 123 } 124 msg.append(" WF_ACTIONS : " + actions.size()); 125 126 for (WorkflowActionBean action : actions) { 127 Services.get().get(InstrumentationService.class).get().incr(INSTRUMENTATION_GROUP, 128 INSTR_CHECK_ACTIONS_COUNTER, 1); 129 if (useXCommand) { 130 queueCallable(new ActionCheckXCommand(action.getId())); 131 } 132 else { 133 queueCallable(new ActionCheckCommand(action.getId())); 134 } 135 } 136 137 } 138 139 /** 140 * check coordinator actions 141 * 142 * @throws CommandException 143 */ 144 private void runCoordActionCheck() throws CommandException { 145 JPAService jpaService = Services.get().get(JPAService.class); 146 if (jpaService == null) { 147 throw new CommandException(ErrorCode.E0610); 148 } 149 150 List<CoordinatorActionBean> cactions; 151 try { 152 cactions = jpaService.execute(new CoordActionsRunningGetJPAExecutor( 153 actionCheckDelay)); 154 } 155 catch (JPAExecutorException je) { 156 throw new CommandException(je); 157 } 158 159 if (cactions == null || cactions.size() == 0) { 160 return; 161 } 162 163 msg.append(" COORD_ACTIONS : " + cactions.size()); 164 165 for (CoordinatorActionBean caction : cactions) { 166 Services.get().get(InstrumentationService.class).get().incr(INSTRUMENTATION_GROUP, 167 INSTR_CHECK_COORD_ACTIONS_COUNTER, 1); 168 if (useXCommand) { 169 queueCallable(new CoordActionCheckXCommand(caction.getId(), actionCheckDelay)); 170 } 171 else { 172 queueCallable(new CoordActionCheckCommand(caction.getId(), actionCheckDelay)); 173 } 174 } 175 176 } 177 178 /** 179 * Adds callables to a list. If the number of callables in the list 180 * reaches {@link ActionCheckerService#CONF_CALLABLE_BATCH_SIZE}, the 181 * entire batch is queued and the callables list is reset. 182 * 183 * @param callable the callable to queue. 184 */ 185 private void queueCallable(XCallable<Void> callable) { 186 if (callables == null) { 187 callables = new ArrayList<XCallable<Void>>(); 188 } 189 callables.add(callable); 190 if (callables.size() == Services.get().getConf().getInt(CONF_CALLABLE_BATCH_SIZE, 10)) { 191 boolean ret = Services.get().get(CallableQueueService.class).queueSerial(callables); 192 if (ret == false) { 193 XLog.getLog(getClass()).warn( 194 "Unable to queue the callables commands for CheckerService. " 195 + "Most possibly command queue is full. Queue size is :" 196 + Services.get().get(CallableQueueService.class).queueSize()); 197 } 198 callables = new ArrayList<XCallable<Void>>(); 199 } 200 } 201 } 202 203 /** 204 * Initializes the Action Check service. 205 * 206 * @param services services instance. 207 */ 208 @Override 209 public void init(Services services) { 210 Configuration conf = services.getConf(); 211 Runnable actionCheckRunnable = new ActionCheckRunnable(conf.getInt(CONF_ACTION_CHECK_DELAY, 600)); 212 services.get(SchedulerService.class).schedule(actionCheckRunnable, 10, 213 conf.getInt(CONF_ACTION_CHECK_INTERVAL, 60), SchedulerService.Unit.SEC); 214 215 if (Services.get().getConf().getBoolean(USE_XCOMMAND, true) == false) { 216 useXCommand = false; 217 } 218 219 } 220 221 /** 222 * Destroy the Action Checker Services. 223 */ 224 @Override 225 public void destroy() { 226 } 227 228 /** 229 * Return the public interface for the action checker service. 230 * 231 * @return {@link ActionCheckerService}. 232 */ 233 @Override 234 public Class<? extends Service> getInterface() { 235 return ActionCheckerService.class; 236 } 237 }