alluxio.master.file.replication.ReplicationChecker.java Source code

Java tutorial

Introduction

Here is the source code for alluxio.master.file.replication.ReplicationChecker.java

Source

/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master.file.replication;

import alluxio.AlluxioURI;
import alluxio.client.job.JobMasterClientPool;
import alluxio.exception.BlockInfoException;
import alluxio.exception.FileDoesNotExistException;
import alluxio.exception.JobDoesNotExistException;
import alluxio.exception.status.ResourceExhaustedException;
import alluxio.exception.status.UnavailableException;
import alluxio.heartbeat.HeartbeatExecutor;
import alluxio.job.replicate.DefaultReplicationHandler;
import alluxio.job.replicate.ReplicationHandler;
import alluxio.master.SafeModeManager;
import alluxio.master.block.BlockMaster;
import alluxio.master.file.meta.InodeFileView;
import alluxio.master.file.meta.InodeTree;
import alluxio.master.file.meta.InodeTree.LockPattern;
import alluxio.master.file.meta.LockedInodePath;
import alluxio.master.file.meta.PersistenceState;
import alluxio.wire.BlockInfo;

import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.concurrent.ThreadSafe;

/**
 * The executor to check block replication level periodically and handle over-replicated and
 * under-replicated blocks correspondingly.
 */
@ThreadSafe
public final class ReplicationChecker implements HeartbeatExecutor {
    private static final Logger LOG = LoggerFactory.getLogger(ReplicationChecker.class);
    private static final long MAX_QUIET_PERIOD_SECONDS = 64;

    /** Handler to the inode tree. */
    private final InodeTree mInodeTree;
    /** Handler to the block master. */
    private final BlockMaster mBlockMaster;
    /** Handler for adjusting block replication level. */
    private final ReplicationHandler mReplicationHandler;
    /** Manager of master safe mode state. */
    private final SafeModeManager mSafeModeManager;

    /**
     * Quiet period for job service flow control (in seconds). When job service refuses starting new
     * jobs, we use exponential backoff to alleviate the job service pressure.
     */
    private long mQuietPeriodSeconds;

    private enum Mode {
        EVICT, REPLICATE
    }

    /**
     * Constructs a new {@link ReplicationChecker} using default (job service) handler to replicate
     * and evict blocks.
     *
     * @param inodeTree inode tree of the filesystem master
     * @param blockMaster block master
     * @param safeModeManager manager of master safe mode state
     * @param jobMasterClientPool job master client pool
     */
    public ReplicationChecker(InodeTree inodeTree, BlockMaster blockMaster, SafeModeManager safeModeManager,
            JobMasterClientPool jobMasterClientPool) {
        this(inodeTree, blockMaster, safeModeManager, new DefaultReplicationHandler(jobMasterClientPool));
    }

    /**
     * Constructs a new {@link ReplicationChecker} with specified replicate and evict handlers (for
     * unit testing).
     *
     * @param inodeTree inode tree of the filesystem master
     * @param blockMaster block master
     * @param safeModeManager manager of master safe mode state
     * @param replicationHandler handler to replicate blocks
     */
    public ReplicationChecker(InodeTree inodeTree, BlockMaster blockMaster, SafeModeManager safeModeManager,
            ReplicationHandler replicationHandler) {
        mInodeTree = inodeTree;
        mBlockMaster = blockMaster;
        mSafeModeManager = safeModeManager;
        mReplicationHandler = replicationHandler;
        mQuietPeriodSeconds = 0;
    }

    /**
     * {@inheritDoc}
     *
     * During this heartbeat, this class will check:
     * <p>
     * (1) Is there any block from the pinned files becomes under replicated (i.e., the number of
     * existing copies is smaller than the target replication min for this file, possibly due to node
     * failures), and schedule replicate jobs to increase the replication level when found;
     *
     * (2) Is there any blocks over replicated, schedule evict jobs to reduce the replication level.
     */
    @Override
    public void heartbeat() throws InterruptedException {
        // skips replication in safe mode when not all workers are registered
        if (mSafeModeManager.isInSafeMode()) {
            return;
        }

        TimeUnit.SECONDS.sleep(mQuietPeriodSeconds);
        Set<Long> inodes;

        // Check the set of files that could possibly be under-replicated
        inodes = mInodeTree.getPinIdSet();
        check(inodes, mReplicationHandler, Mode.REPLICATE);

        // Check the set of files that could possibly be over-replicated
        inodes = mInodeTree.getReplicationLimitedFileIds();
        check(inodes, mReplicationHandler, Mode.EVICT);
    }

    @Override
    public void close() {
        // Nothing to clean up
    }

    private void check(Set<Long> inodes, ReplicationHandler handler, Mode mode) {
        Set<Long> lostBlocks = mBlockMaster.getLostBlocks();
        Set<Triple<AlluxioURI, Long, Integer>> requests = new HashSet<>();
        for (long inodeId : inodes) {
            // TODO(binfan): calling lockFullInodePath locks the entire path from root to the target
            // file and may increase lock contention in this tree. Investigate if we could avoid
            // locking the entire path but just the inode file since this access is read-only.
            try (LockedInodePath inodePath = mInodeTree.lockFullInodePath(inodeId, LockPattern.READ)) {
                InodeFileView file = inodePath.getInodeFile();
                for (long blockId : file.getBlockIds()) {
                    BlockInfo blockInfo = null;
                    try {
                        blockInfo = mBlockMaster.getBlockInfo(blockId);
                    } catch (BlockInfoException e) {
                        // Cannot find this block in Alluxio from BlockMaster, possibly persisted in UFS
                    } catch (UnavailableException e) {
                        // The block master is not available, wait for the next heartbeat
                        LOG.warn("The block master is not available: {}", e.getMessage());
                        return;
                    }
                    int currentReplicas = (blockInfo == null) ? 0 : blockInfo.getLocations().size();
                    switch (mode) {
                    case EVICT:
                        int maxReplicas = file.getReplicationMax();
                        if (file.getPersistenceState() == PersistenceState.TO_BE_PERSISTED
                                && file.getReplicationDurable() > maxReplicas) {
                            maxReplicas = file.getReplicationDurable();
                        }
                        if (currentReplicas > maxReplicas) {
                            requests.add(new ImmutableTriple<>(inodePath.getUri(), blockId,
                                    currentReplicas - maxReplicas));
                        }
                        break;
                    case REPLICATE:
                        int minReplicas = file.getReplicationMin();
                        if (file.getPersistenceState() == PersistenceState.TO_BE_PERSISTED
                                && file.getReplicationDurable() > minReplicas) {
                            minReplicas = file.getReplicationDurable();
                        }
                        if (currentReplicas < minReplicas) {
                            // if this file is not persisted and block master thinks it is lost, no effort made
                            if (!file.isPersisted() && lostBlocks.contains(blockId)) {
                                continue;
                            }
                            requests.add(new ImmutableTriple<>(inodePath.getUri(), blockId,
                                    minReplicas - currentReplicas));
                        }
                        break;
                    default:
                        LOG.warn("Unexpected replication mode {}.", mode);
                    }
                }
            } catch (FileDoesNotExistException e) {
                LOG.warn("Failed to check replication level for inode id {} : {}", inodeId, e.getMessage());
            }
        }
        for (Triple<AlluxioURI, Long, Integer> entry : requests) {
            AlluxioURI uri = entry.getLeft();
            long blockId = entry.getMiddle();
            int numReplicas = entry.getRight();
            try {
                switch (mode) {
                case EVICT:
                    handler.evict(uri, blockId, numReplicas);
                    mQuietPeriodSeconds /= 2;
                    break;
                case REPLICATE:
                    handler.replicate(uri, blockId, numReplicas);
                    mQuietPeriodSeconds /= 2;
                    break;
                default:
                    LOG.warn("Unexpected replication mode {}.", mode);
                }
            } catch (JobDoesNotExistException | ResourceExhaustedException e) {
                LOG.warn("The job service is busy, will retry later. {}", e.toString());
                mQuietPeriodSeconds = (mQuietPeriodSeconds == 0) ? 1
                        : Math.min(MAX_QUIET_PERIOD_SECONDS, mQuietPeriodSeconds * 2);
                return;
            } catch (UnavailableException e) {
                LOG.warn("Unable to complete the replication check: {}, will retry later.", e.getMessage());
                return;
            } catch (Exception e) {
                LOG.warn("Unexpected exception encountered when starting a replication / eviction job (uri={},"
                        + " block ID={}, num replicas={}) : {}", uri, blockId, numReplicas, e.getMessage());
                LOG.debug("Exception: ", e);
            }
        }
    }
}