org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.RamDiskAsyncLazyPersistService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.RamDiskAsyncLazyPersistService.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.hdfs.server.datanode.fsdataset.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.server.datanode.DataNode;
import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeReference;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * This class is a container of multiple thread pools, one for each non-RamDisk
 * volume with a maximum thread count of 1 so that we can schedule async lazy
 * persist operations easily with volume arrival and departure handled.
 *
 * This class and {@link org.apache.hadoop.util.AsyncDiskService} are similar.
 * They should be combined.
 */
class RamDiskAsyncLazyPersistService {
    public static final Log LOG = LogFactory.getLog(RamDiskAsyncLazyPersistService.class);

    // ThreadPool core pool size
    private static final int CORE_THREADS_PER_VOLUME = 1;
    // ThreadPool maximum pool size
    private static final int MAXIMUM_THREADS_PER_VOLUME = 1;
    // ThreadPool keep-alive time for threads over core pool size
    private static final long THREADS_KEEP_ALIVE_SECONDS = 60;

    private final DataNode datanode;
    private final ThreadGroup threadGroup;
    private Map<File, ThreadPoolExecutor> executors = new HashMap<File, ThreadPoolExecutor>();
    private final static HdfsConfiguration EMPTY_HDFS_CONF = new HdfsConfiguration();

    /**
     * Create a RamDiskAsyncLazyPersistService with a set of volumes (specified by their
     * root directories).
     *
     * The RamDiskAsyncLazyPersistService uses one ThreadPool per volume to do the async
     * disk operations.
     */
    RamDiskAsyncLazyPersistService(DataNode datanode) {
        this.datanode = datanode;
        this.threadGroup = new ThreadGroup(getClass().getSimpleName());
    }

    private void addExecutorForVolume(final File volume) {
        ThreadFactory threadFactory = new ThreadFactory() {

            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(threadGroup, r);
                t.setName("Async RamDisk lazy persist worker for volume " + volume);
                return t;
            }
        };

        ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_THREADS_PER_VOLUME, MAXIMUM_THREADS_PER_VOLUME,
                THREADS_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);

        // This can reduce the number of running threads
        executor.allowCoreThreadTimeOut(true);
        executors.put(volume, executor);
    }

    /**
     * Starts AsyncLazyPersistService for a new volume
     * @param volume the root of the new data volume.
     */
    synchronized void addVolume(File volume) {
        if (executors == null) {
            throw new RuntimeException("AsyncLazyPersistService is already shutdown");
        }
        ThreadPoolExecutor executor = executors.get(volume);
        if (executor != null) {
            throw new RuntimeException("Volume " + volume + " is already existed.");
        }
        addExecutorForVolume(volume);
    }

    /**
     * Stops AsyncLazyPersistService for a volume.
     * @param volume the root of the volume.
     */
    synchronized void removeVolume(File volume) {
        if (executors == null) {
            throw new RuntimeException("AsyncDiskService is already shutdown");
        }
        ThreadPoolExecutor executor = executors.get(volume);
        if (executor == null) {
            throw new RuntimeException("Can not find volume " + volume + " to remove.");
        } else {
            executor.shutdown();
            executors.remove(volume);
        }
    }

    /**
     * Query if the thread pool exist for the volume
     * @param volume the root of a volume
     * @return true if there is one thread pool for the volume
     *         false otherwise
     */
    synchronized boolean queryVolume(File volume) {
        if (executors == null) {
            throw new RuntimeException("AsyncLazyPersistService is already shutdown");
        }
        ThreadPoolExecutor executor = executors.get(volume);
        return (executor != null);
    }

    /**
     * Execute the task sometime in the future, using ThreadPools.
     */
    synchronized void execute(File root, Runnable task) {
        if (executors == null) {
            throw new RuntimeException("AsyncLazyPersistService is already shutdown");
        }
        ThreadPoolExecutor executor = executors.get(root);
        if (executor == null) {
            throw new RuntimeException("Cannot find root " + root + " for execution of task " + task);
        } else {
            executor.execute(task);
        }
    }

    /**
     * Gracefully shut down all ThreadPool. Will wait for all lazy persist
     * tasks to finish.
     */
    synchronized void shutdown() {
        if (executors == null) {
            LOG.warn("AsyncLazyPersistService has already shut down.");
        } else {
            LOG.info("Shutting down all async lazy persist service threads");

            for (Map.Entry<File, ThreadPoolExecutor> e : executors.entrySet()) {
                e.getValue().shutdown();
            }
            // clear the executor map so that calling execute again will fail.
            executors = null;
            LOG.info("All async lazy persist service threads have been shut down");
        }
    }

    /**
     * Asynchronously lazy persist the block from the RamDisk to Disk.
     */
    void submitLazyPersistTask(String bpId, long blockId, long genStamp, long creationTime, File metaFile,
            File blockFile, FsVolumeReference target) throws IOException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("LazyWriter schedule async task to persist RamDisk block pool id: " + bpId + " block id: "
                    + blockId);
        }

        FsVolumeImpl volume = (FsVolumeImpl) target.getVolume();
        File lazyPersistDir = volume.getLazyPersistDir(bpId);
        if (!lazyPersistDir.exists() && !lazyPersistDir.mkdirs()) {
            FsDatasetImpl.LOG.warn("LazyWriter failed to create " + lazyPersistDir);
            throw new IOException(
                    "LazyWriter fail to find or create lazy persist dir: " + lazyPersistDir.toString());
        }

        ReplicaLazyPersistTask lazyPersistTask = new ReplicaLazyPersistTask(bpId, blockId, genStamp, creationTime,
                blockFile, metaFile, target, lazyPersistDir);
        execute(volume.getCurrentDir(), lazyPersistTask);
    }

    class ReplicaLazyPersistTask implements Runnable {
        final String bpId;
        final long blockId;
        final long genStamp;
        final long creationTime;
        final File blockFile;
        final File metaFile;
        final FsVolumeReference targetVolume;
        final File lazyPersistDir;

        ReplicaLazyPersistTask(String bpId, long blockId, long genStamp, long creationTime, File blockFile,
                File metaFile, FsVolumeReference targetVolume, File lazyPersistDir) {
            this.bpId = bpId;
            this.blockId = blockId;
            this.genStamp = genStamp;
            this.creationTime = creationTime;
            this.blockFile = blockFile;
            this.metaFile = metaFile;
            this.targetVolume = targetVolume;
            this.lazyPersistDir = lazyPersistDir;
        }

        @Override
        public String toString() {
            // Called in AsyncLazyPersistService.execute for displaying error messages.
            return "LazyWriter async task of persist RamDisk block pool id:" + bpId + " block pool id: " + blockId
                    + " with block file " + blockFile + " and meta file " + metaFile + " to target volume "
                    + targetVolume;
        }

        @Override
        public void run() {
            boolean succeeded = false;
            final FsDatasetImpl dataset = (FsDatasetImpl) datanode.getFSDataset();
            try (FsVolumeReference ref = this.targetVolume) {
                int smallBufferSize = DFSUtil.getSmallBufferSize(EMPTY_HDFS_CONF);
                // No FsDatasetImpl lock for the file copy
                File targetFiles[] = FsDatasetImpl.copyBlockFiles(blockId, genStamp, metaFile, blockFile,
                        lazyPersistDir, true, smallBufferSize);

                // Lock FsDataSetImpl during onCompleteLazyPersist callback
                dataset.onCompleteLazyPersist(bpId, blockId, creationTime, targetFiles,
                        (FsVolumeImpl) targetVolume.getVolume());
                succeeded = true;
            } catch (Exception e) {
                FsDatasetImpl.LOG.warn("LazyWriter failed to async persist RamDisk block pool id: " + bpId
                        + "block Id: " + blockId, e);
            } finally {
                if (!succeeded) {
                    dataset.onFailLazyPersist(bpId, blockId);
                }
            }
        }
    }
}