org.apache.hadoop.mapred.MiniMRCluster.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.mapred.MiniMRCluster.java

Source

/**
 * 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.hadoop.mapred;

import java.io.File;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.net.DNSToSwitchMapping;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.net.NetworkTopology;
import org.apache.hadoop.net.StaticMapping;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;

/**
 * This class creates a single-process Map-Reduce cluster for junit testing.
 * One thread is created for each server.
 */
public class MiniMRCluster {
    private static final Log LOG = LogFactory.getLog(MiniMRCluster.class);

    private Thread jobTrackerThread;
    private JobTrackerRunner jobTracker;

    private int jobTrackerPort = 0;
    private int taskTrackerPort = 0;
    private int jobTrackerInfoPort = 0;
    private int numTaskTrackers;

    private List<TaskTrackerRunner> taskTrackerList = new ArrayList<TaskTrackerRunner>();
    private List<Thread> taskTrackerThreadList = new ArrayList<Thread>();

    private String namenode;
    private UserGroupInformation ugi = null;
    private JobConf conf;
    private int numTrackerToExclude;

    private JobConf job;

    /**
     * An inner class that runs a job tracker.
     */
    public class JobTrackerRunner implements Runnable {
        private JobTracker tracker = null;
        private volatile boolean isActive = true;

        JobConf jc = null;

        public JobTrackerRunner(JobConf conf) {
            jc = conf;
        }

        public boolean isUp() {
            return (tracker != null);
        }

        public boolean isActive() {
            return isActive;
        }

        public int getJobTrackerPort() {
            return tracker.getTrackerPort();
        }

        public int getJobTrackerInfoPort() {
            return tracker.getInfoPort();
        }

        public JobTracker getJobTracker() {
            return tracker;
        }

        /**
         * Create the job tracker and run it.
         */
        public void run() {
            try {
                jc = (jc == null) ? createJobConf() : createJobConf(jc);
                File f = new File("build/test/mapred/local").getAbsoluteFile();
                jc.set("mapred.local.dir", f.getAbsolutePath());
                jc.setClass("topology.node.switch.mapping.impl", StaticMapping.class, DNSToSwitchMapping.class);
                final String id = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                if (ugi == null) {
                    ugi = UserGroupInformation.getCurrentUser();
                }
                tracker = ugi.doAs(new PrivilegedExceptionAction<JobTracker>() {
                    public JobTracker run() throws InterruptedException, IOException {
                        return JobTracker.startTracker(jc, id);
                    }
                });
                tracker.offerService();
            } catch (Throwable e) {
                LOG.error("Job tracker crashed", e);
                isActive = false;
            }
        }

        /**
         * Shutdown the job tracker and wait for it to finish.
         */
        public void shutdown() {
            try {
                if (tracker != null) {
                    tracker.stopTracker();
                }
            } catch (Throwable e) {
                LOG.error("Problem shutting down job tracker", e);
            }
            isActive = false;
        }
    }

    /**
     * An inner class to run the task tracker.
     */
    class TaskTrackerRunner implements Runnable {
        volatile TaskTracker tt;
        int trackerId;
        // the localDirs for this taskTracker
        String[] localDirs;
        volatile boolean isInitialized = false;
        volatile boolean isDead = false;
        int numDir;

        TaskTrackerRunner(int trackerId, int numDir, String hostname, JobConf cfg) throws IOException {
            this.trackerId = trackerId;
            this.numDir = numDir;
            localDirs = new String[numDir];
            final JobConf conf;
            if (cfg == null) {
                conf = createJobConf();
            } else {
                conf = createJobConf(cfg);
            }
            if (hostname != null) {
                conf.set("slave.host.name", hostname);
            }
            conf.set("mapred.task.tracker.http.address", "0.0.0.0:0");
            conf.set("mapred.task.tracker.report.address", "127.0.0.1:" + taskTrackerPort);
            File localDirBase = new File(conf.get("mapred.local.dir")).getAbsoluteFile();
            localDirBase.mkdirs();
            StringBuffer localPath = new StringBuffer();
            for (int i = 0; i < numDir; ++i) {
                File ttDir = new File(localDirBase, Integer.toString(trackerId) + "_" + i);
                if (!ttDir.mkdirs()) {
                    if (!ttDir.isDirectory()) {
                        throw new IOException("Mkdirs failed to create " + ttDir);
                    }
                }
                localDirs[i] = ttDir.toString();
                if (i != 0) {
                    localPath.append(",");
                }
                localPath.append(localDirs[i]);
            }
            conf.set("mapred.local.dir", localPath.toString());
            LOG.info("mapred.local.dir is " + localPath);
            try {
                tt = ugi.doAs(new PrivilegedExceptionAction<TaskTracker>() {
                    public TaskTracker run() throws InterruptedException, IOException {
                        return createTaskTracker(conf);
                    }
                });
                isInitialized = true;
            } catch (Throwable e) {
                isDead = true;
                tt = null;
                LOG.error("task tracker " + trackerId + " crashed", e);
            }
        }

        /**
         * Creates a default {@link TaskTracker} using the conf passed. 
         */
        TaskTracker createTaskTracker(JobConf conf) throws InterruptedException, IOException {
            return new TaskTracker(conf);
        }

        /**
         * Create and run the task tracker.
         */
        public void run() {
            try {
                if (tt != null) {
                    tt.run();
                }
            } catch (Throwable e) {
                isDead = true;
                tt = null;
                LOG.error("task tracker " + trackerId + " crashed", e);
            }
        }

        /**
         * Get the local dir for this TaskTracker.
         * This is there so that we do not break
         * previous tests. 
         * @return the absolute pathname
         */
        public String getLocalDir() {
            return localDirs[0];
        }

        public String[] getLocalDirs() {
            return localDirs;
        }

        public TaskTracker getTaskTracker() {
            return tt;
        }

        /**
         * Shut down the server and wait for it to finish.
         */
        public void shutdown() {
            if (tt != null) {
                try {
                    tt.shutdown();
                } catch (Throwable e) {
                    LOG.error("task tracker " + trackerId + " could not shut down", e);
                }
            }
        }
    }

    /**
     * Get the local directory for the Nth task tracker
     * @param taskTracker the index of the task tracker to check
     * @return the absolute pathname of the local dir
     */
    public String getTaskTrackerLocalDir(int taskTracker) {
        return (taskTrackerList.get(taskTracker)).getLocalDir();
    }

    /**
     * Get all the local directories for the Nth task tracker
     * @param taskTracker the index of the task tracker to check
     * @return array of local dirs
     */
    public String[] getTaskTrackerLocalDirs(int taskTracker) {
        return (taskTrackerList.get(taskTracker)).getLocalDirs();
    }

    public JobTrackerRunner getJobTrackerRunner() {
        return jobTracker;
    }

    TaskTrackerRunner getTaskTrackerRunner(int id) {
        return taskTrackerList.get(id);
    }

    /**
     * Get the number of task trackers in the cluster
     */
    public int getNumTaskTrackers() {
        return taskTrackerList.size();
    }

    /**
     * Sets inline cleanup threads to all task trackers sothat deletion of
     * temporary files/dirs happen inline
     */
    public void setInlineCleanupThreads() {
        for (int i = 0; i < getNumTaskTrackers(); i++) {
            getTaskTrackerRunner(i).getTaskTracker().setCleanupThread(new UtilsForTests.InlineCleanupQueue());
        }
    }

    /**
     * Wait until the system is idle.
     */
    public void waitUntilIdle() {
        waitTaskTrackers();

        JobClient client;
        try {
            client = new JobClient(job);
            ClusterStatus status = client.getClusterStatus();
            while (status.getTaskTrackers() + numTrackerToExclude < taskTrackerList.size()) {
                for (TaskTrackerRunner runner : taskTrackerList) {
                    if (runner.isDead) {
                        throw new RuntimeException("TaskTracker is dead");
                    }
                }
                Thread.sleep(1000);
                status = client.getClusterStatus();
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }

    }

    private void waitTaskTrackers() {
        for (Iterator<TaskTrackerRunner> itr = taskTrackerList.iterator(); itr.hasNext();) {
            TaskTrackerRunner runner = itr.next();
            while (!runner.isDead && (!runner.isInitialized || !runner.tt.isIdle())) {
                if (!runner.isInitialized) {
                    LOG.info("Waiting for task tracker to start.");
                } else {
                    LOG.info("Waiting for task tracker " + runner.tt.getName() + " to be idle.");
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

    /** 
     * Get the actual rpc port used.
     */
    public int getJobTrackerPort() {
        return jobTrackerPort;
    }

    public JobConf createJobConf() {
        return createJobConf(new JobConf());
    }

    public JobConf createJobConf(JobConf conf) {
        if (conf == null) {
            conf = new JobConf();
        }
        return configureJobConf(conf, namenode, jobTrackerPort, jobTrackerInfoPort, ugi);
    }

    static JobConf configureJobConf(JobConf conf, String namenode, int jobTrackerPort, int jobTrackerInfoPort,
            UserGroupInformation ugi) {
        JobConf result = new JobConf(conf);
        FileSystem.setDefaultUri(result, namenode);
        result.set("mapred.job.tracker", "localhost:" + jobTrackerPort);
        result.set("mapred.job.tracker.http.address", "127.0.0.1:" + jobTrackerInfoPort);
        // for debugging have all task output sent to the test output
        JobClient.setTaskOutputFilter(result, JobClient.TaskStatusFilter.ALL);
        return result;
    }

    /**
     * Create the config and the cluster.
     * @param numTaskTrackers no. of tasktrackers in the cluster
     * @param namenode the namenode
     * @param numDir no. of directories
     * @throws IOException
     */
    public MiniMRCluster(int numTaskTrackers, String namenode, int numDir, String[] racks, String[] hosts)
            throws IOException {
        this(0, 0, numTaskTrackers, namenode, numDir, racks, hosts);
    }

    /**
     * Create the config and the cluster.
     * @param numTaskTrackers no. of tasktrackers in the cluster
     * @param namenode the namenode
     * @param numDir no. of directories
     * @param racks Array of racks
     * @param hosts Array of hosts in the corresponding racks
     * @param conf Default conf for the jobtracker
     * @throws IOException
     */
    public MiniMRCluster(int numTaskTrackers, String namenode, int numDir, String[] racks, String[] hosts,
            JobConf conf) throws IOException {
        this(0, 0, numTaskTrackers, namenode, numDir, racks, hosts, null, conf);
    }

    /**
     * Create the config and the cluster.
     * @param numTaskTrackers no. of tasktrackers in the cluster
     * @param namenode the namenode
     * @param numDir no. of directories
     * @throws IOException
     */
    public MiniMRCluster(int numTaskTrackers, String namenode, int numDir) throws IOException {
        this(0, 0, numTaskTrackers, namenode, numDir);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir)
            throws IOException {
        this(jobTrackerPort, taskTrackerPort, numTaskTrackers, namenode, numDir, null);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir,
            String[] racks) throws IOException {
        this(jobTrackerPort, taskTrackerPort, numTaskTrackers, namenode, numDir, racks, null);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir,
            String[] racks, String[] hosts) throws IOException {
        this(jobTrackerPort, taskTrackerPort, numTaskTrackers, namenode, numDir, racks, hosts, null);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir,
            String[] racks, String[] hosts, UserGroupInformation ugi) throws IOException {
        this(jobTrackerPort, taskTrackerPort, numTaskTrackers, namenode, numDir, racks, hosts, ugi, null);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir,
            String[] racks, String[] hosts, UserGroupInformation ugi, JobConf conf) throws IOException {
        this(jobTrackerPort, taskTrackerPort, numTaskTrackers, namenode, numDir, racks, hosts, ugi, conf, 0);
    }

    public MiniMRCluster(int jobTrackerPort, int taskTrackerPort, int numTaskTrackers, String namenode, int numDir,
            String[] racks, String[] hosts, UserGroupInformation ugi, JobConf conf, int numTrackerToExclude)
            throws IOException {
        if (racks != null && racks.length < numTaskTrackers) {
            LOG.error("Invalid number of racks specified. It should be at least "
                    + "equal to the number of tasktrackers");
            shutdown();
        }
        if (hosts != null && numTaskTrackers > hosts.length) {
            throw new IllegalArgumentException("The length of hosts [" + hosts.length
                    + "] is less than the number of tasktrackers [" + numTaskTrackers + "].");
        }

        //Generate rack names if required
        if (racks == null) {
            System.out.println("Generating rack names for tasktrackers");
            racks = new String[numTaskTrackers];
            for (int i = 0; i < racks.length; ++i) {
                racks[i] = NetworkTopology.DEFAULT_RACK;
            }
        }

        //Generate some hostnames if required
        if (hosts == null) {
            System.out.println("Generating host names for tasktrackers");
            hosts = new String[numTaskTrackers];
            for (int i = 0; i < numTaskTrackers; i++) {
                hosts[i] = "host" + i + ".foo.com";
            }
        }
        this.jobTrackerPort = jobTrackerPort;
        this.taskTrackerPort = taskTrackerPort;
        this.jobTrackerInfoPort = 0;
        this.numTaskTrackers = 0;
        this.namenode = namenode;
        this.ugi = ugi;
        this.conf = conf; // this is the conf the mr starts with
        this.numTrackerToExclude = numTrackerToExclude;

        // start the jobtracker
        startJobTracker();

        // Create the TaskTrackers
        for (int idx = 0; idx < numTaskTrackers; idx++) {
            String rack = null;
            String host = null;
            if (racks != null) {
                rack = racks[idx];
            }
            if (hosts != null) {
                host = hosts[idx];
            }

            startTaskTracker(host, rack, idx, numDir);
        }

        this.job = createJobConf(conf);
        waitUntilIdle();
    }

    public UserGroupInformation getUgi() {
        return ugi;
    }

    /**
     * Get the task completion events
     */
    public TaskCompletionEvent[] getTaskCompletionEvents(JobID id, int from, int max) throws IOException {
        return jobTracker.getJobTracker().getTaskCompletionEvents(id, from, max);
    }

    /**
     * Change the job's priority
     * 
     * @throws IOException
     * @throws AccessControlException
     */
    public void setJobPriority(JobID jobId, JobPriority priority) throws AccessControlException, IOException {
        jobTracker.getJobTracker().setJobPriority(jobId, priority);
    }

    /**
     * Get the job's priority
     */
    public JobPriority getJobPriority(JobID jobId) {
        return jobTracker.getJobTracker().getJob(jobId).getPriority();
    }

    /**
     * Get the job finish time
     */
    public long getJobFinishTime(JobID jobId) {
        return jobTracker.getJobTracker().getJob(jobId).getFinishTime();
    }

    /**
     * Init the job
     */
    public void initializeJob(JobID jobId) throws IOException {
        JobInProgress job = jobTracker.getJobTracker().getJob(jobId);
        jobTracker.getJobTracker().initJob(job);
    }

    /**
     * Get the events list at the tasktracker
     */
    public MapTaskCompletionEventsUpdate getMapTaskCompletionEventsUpdates(int index, JobID jobId, int max)
            throws IOException {
        String jtId = jobTracker.getJobTracker().getTrackerIdentifier();
        TaskAttemptID dummy = new TaskAttemptID(jtId, jobId.getId(), false, 0, 0);
        return taskTrackerList.get(index).getTaskTracker().getMapCompletionEvents(jobId, 0, max, dummy, null);
    }

    /**
     * Get jobtracker conf
     */
    public JobConf getJobTrackerConf() {
        return this.conf;
    }

    /**
     * Get num events recovered
     */
    public int getNumEventsRecovered() {
        return jobTracker.getJobTracker().recoveryManager.totalEventsRecovered();
    }

    public int getFaultCount(String hostName) {
        return jobTracker.getJobTracker().getFaultCount(hostName);
    }

    /**
     * Start the jobtracker.
     */
    public void startJobTracker() {
        startJobTracker(true);
    }

    void startJobTracker(boolean wait) {
        //  Create the JobTracker
        jobTracker = new JobTrackerRunner(conf);
        jobTrackerThread = new Thread(jobTracker);

        jobTrackerThread.start();

        if (!wait) {
            return;
        }

        while (jobTracker.isActive() && !jobTracker.isUp()) {
            try { // let daemons get started
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }

        // is the jobtracker has started then wait for it to init
        ClusterStatus status = null;
        if (jobTracker.isUp()) {
            status = jobTracker.getJobTracker().getClusterStatus(false);
            while (jobTracker.isActive() && status.getJobTrackerState() == JobTracker.State.INITIALIZING) {
                try {
                    LOG.info("JobTracker still initializing. Waiting.");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                status = jobTracker.getJobTracker().getClusterStatus(false);
            }
        }

        if (!jobTracker.isActive()) {
            // return if jobtracker has crashed
            return;
        }

        // Set the configuration for the task-trackers
        this.jobTrackerPort = jobTracker.getJobTrackerPort();
        this.jobTrackerInfoPort = jobTracker.getJobTrackerInfoPort();
    }

    /**
     * Kill the jobtracker.
     */
    public void stopJobTracker() {
        //jobTracker.exit(-1);
        jobTracker.shutdown();

        jobTrackerThread.interrupt();
        try {
            jobTrackerThread.join();
        } catch (InterruptedException ex) {
            LOG.error("Problem waiting for job tracker to finish", ex);
        }
    }

    /**
     * Kill the tasktracker.
     */
    public void stopTaskTracker(int id) {
        TaskTrackerRunner tracker = taskTrackerList.remove(id);
        tracker.shutdown();

        Thread thread = taskTrackerThreadList.remove(id);
        thread.interrupt();

        try {
            thread.join();
            // This will break the wait until idle loop
            tracker.isDead = true;
            --numTaskTrackers;
        } catch (InterruptedException ex) {
            LOG.error("Problem waiting for task tracker to finish", ex);
        }
    }

    /**
     * Start the tasktracker.
     */
    public void startTaskTracker(String host, String rack, int idx, int numDir) throws IOException {
        if (rack != null) {
            StaticMapping.addNodeToRack(host, rack);
        }
        if (host != null) {
            NetUtils.addStaticResolution(host, "localhost");
        }
        TaskTrackerRunner taskTracker;
        taskTracker = new TaskTrackerRunner(idx, numDir, host, conf);

        addTaskTracker(taskTracker);
    }

    /**
     * Add a tasktracker to the Mini-MR cluster.
     */
    void addTaskTracker(TaskTrackerRunner taskTracker) {
        Thread taskTrackerThread = new Thread(taskTracker);
        taskTrackerList.add(taskTracker);
        taskTrackerThreadList.add(taskTrackerThread);
        taskTrackerThread.start();
        ++numTaskTrackers;
    }

    /**
     * Get the tasktrackerID in MiniMRCluster with given trackerName.
     */
    int getTaskTrackerID(String trackerName) {
        for (int id = 0; id < numTaskTrackers; id++) {
            if (taskTrackerList.get(id).getTaskTracker().getName().equals(trackerName)) {
                return id;
            }
        }
        return -1;
    }

    /**
     * Shut down the servers.
     */
    public void shutdown() {
        try {
            waitTaskTrackers();
            for (int idx = 0; idx < numTaskTrackers; idx++) {
                TaskTrackerRunner taskTracker = taskTrackerList.get(idx);
                Thread taskTrackerThread = taskTrackerThreadList.get(idx);
                taskTracker.shutdown();
                taskTrackerThread.interrupt();
                try {
                    taskTrackerThread.join();
                } catch (InterruptedException ex) {
                    LOG.error("Problem shutting down task tracker", ex);
                }
            }
            stopJobTracker();
        } finally {
            File configDir = new File("build", "minimr");
            File siteFile = new File(configDir, "mapred-site.xml");
            siteFile.delete();
        }
    }

    public static void main(String[] args) throws IOException {
        LOG.info("Bringing up Jobtracker and tasktrackers.");
        MiniMRCluster mr = new MiniMRCluster(4, "file:///", 1);
        LOG.info("JobTracker and TaskTrackers are up.");
        mr.shutdown();
        LOG.info("JobTracker and TaskTrackers brought down.");
    }
}