/** * Copyright 2014 Yahoo! Inc. Licensed 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. See accompanying * LICENSE file. */ package com.yahoo.sql4d.indexeragent.actors; import akka.actor.ActorRef; import akka.actor.Cancellable; import akka.actor.Props; import akka.actor.Scheduler; import akka.actor.UntypedActor; import akka.routing.RoundRobinPool; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.concurrent.duration.Duration; import static com.yahoo.sql4d.indexeragent.Agent.*; import java.io.File; import java.io.FilenameFilter; import static com.yahoo.sql4d.indexeragent.actors.MessageTypes.*; import com.yahoo.sql4d.indexeragent.sql.SqlFileSniffer; import com.yahoo.sql4d.indexeragent.util.FileSniffer; import java.nio.file.Paths; import scala.concurrent.duration.FiniteDuration; /** * Upon receiving "startSignal" the MainActor actor starts schedulers which schedules tasks(if any) to Workers by sending typed Work messages. * @author srikalyan */ public class MainActor extends UntypedActor { private static final Logger log = LoggerFactory.getLogger(MainActor.class); private static final int INITIAL_WORK_GENERATE_DELAY = 1;// In secs private static final int INITIAL_WORK_EXECUTE_DELAY = 5;// In secs private static final int INITIAL_WORK_TRACKER_DELAY = 10;// In secs private final int WORK_GENERATE_INTERVAL;// In secs private final int WORK_TRACKER_INTERVAL;// In secs private final int MAX_CONCURRENCY;// # of workers. private static ActorRef workerRouter; private final Scheduler scheduler; // The following 3 are crons. private Cancellable workInstanceGenerator; private Cancellable workExecutor;// Is also a Cancellable. private Cancellable workProgressTracker; // The following is an observer(observes changes to druid sql files) private FileSniffer sqlSniffer;//Allows to dynamically add/remove/modify more tables by adding sql files without bringing down Agent. public MainActor() { MAX_CONCURRENCY = getNumWorkers(); workerRouter = getContext().actorOf(Props.create(WorkerActor.class). withRouter(new RoundRobinPool(MAX_CONCURRENCY)), "workerRouter"); scheduler = getContext().system().scheduler(); WORK_GENERATE_INTERVAL = getWorkGenerateInterval(); WORK_TRACKER_INTERVAL = getWorkTrackInterval(); } @Override public void postStop() { } @Override public void onReceive(Object message) throws Exception { if (!(message instanceof MessageTypes)) { unhandled(message); return; } switch((MessageTypes)message) { case BOOT_FROM_SQLS: log.info("Booting off template sqls from {}", getDsqlsPath()); sqlSniffer = new SqlFileSniffer(getDsqlsPath()); sqlSniffer.startSniffing(); bootFromDsqls(getDsqlsPath()); getSelf().tell(START_TICKING, getSelf()); break; case START_TICKING: log.info("Started ticking ..."); workInstanceGenerator = scheduleCron(INITIAL_WORK_GENERATE_DELAY, WORK_GENERATE_INTERVAL, GENERATE_WORK); workExecutor = scheduleThrottler(INITIAL_WORK_EXECUTE_DELAY, getWorkExecuteMsgsPerSec(), getWorkExecuteMaxAtGivenTime()); workProgressTracker = scheduleCron(INITIAL_WORK_TRACKER_DELAY, WORK_TRACKER_INTERVAL, TRACK_WORK); break; case GENERATE_WORK: workerRouter.tell(GENERATE_WORK, getSelf()); break; case EXECUTE_WORK: workerRouter.tell(EXECUTE_WORK, getSelf()); break; case TRACK_WORK: workerRouter.tell(TRACK_WORK, getSelf()); break; case STOP_TICKING: log.info("Stopped ticking ..."); workInstanceGenerator.cancel(); workExecutor.cancel(); workProgressTracker.cancel(); sqlSniffer.stopSniffing(); break; default: unhandled(message); } } /** * Schedules messages ever interval seconds. * @param initialDelay * @param interval * @param message * @return */ private Cancellable scheduleCron(int initialDelay, int interval, MessageTypes message) { return scheduler.schedule(secs(initialDelay), secs(interval), getSelf(), message, getContext().dispatcher(), null); } private Cancellable scheduleThrottler(final int initialDelay, final int msgsPerSecond, final int maxMsgsAtGivenTime) { return new Throttler(initialDelay, msgsPerSecond, maxMsgsAtGivenTime) { @Override public int getInProgressActionCount() { return (int)(db().getInprogressTasksCount()); } @Override public void runAction() { workerRouter.tell(EXECUTE_WORK, getSelf()); } }.startThrottling(); } /** * Read off a bunch of sql files expecting insert statements within them. */ private void bootFromDsqls(String path) { File[] files = new File(path).listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".sql"); } }); for (File file:files) {//In this context only add/update is cared for. sqlSniffer.onCreate(Paths.get(file.toURI())); } } private FiniteDuration secs(int sec) { return Duration.create(sec, TimeUnit.SECONDS); } }