| /** |
| * 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.namenode; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.TreeSet; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hdfs.DFSConfigKeys; |
| import org.apache.hadoop.hdfs.server.namenode.FSImageStorageInspector.FSImageFile; |
| import org.apache.hadoop.hdfs.server.namenode.FileJournalManager.EditLogFile; |
| import org.apache.hadoop.hdfs.util.MD5FileUtils; |
| |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * The NNStorageRetentionManager is responsible for inspecting the storage |
| * directories of the NN and enforcing a retention policy on checkpoints |
| * and edit logs. |
| * |
| * It delegates the actual removal of files to a StoragePurger |
| * implementation, which might delete the files or instead copy them to |
| * a filer or HDFS for later analysis. |
| */ |
| public class NNStorageRetentionManager { |
| |
| private final int numCheckpointsToRetain; |
| private static final Log LOG = LogFactory.getLog( |
| NNStorageRetentionManager.class); |
| private final NNStorage storage; |
| private final StoragePurger purger; |
| private final FSEditLog editLog; |
| |
| public NNStorageRetentionManager( |
| Configuration conf, |
| NNStorage storage, |
| FSEditLog editLog, |
| StoragePurger purger) { |
| this.numCheckpointsToRetain = conf.getInt( |
| DFSConfigKeys.DFS_NAMENODE_NUM_CHECKPOINTS_RETAINED_KEY, |
| DFSConfigKeys.DFS_NAMENODE_NUM_CHECKPOINTS_RETAINED_DEFAULT); |
| this.storage = storage; |
| this.editLog = editLog; |
| this.purger = purger; |
| } |
| |
| public NNStorageRetentionManager(Configuration conf, NNStorage storage, |
| FSEditLog editLog) { |
| this(conf, storage, editLog, new DeletionStoragePurger()); |
| } |
| |
| public void purgeOldStorage() throws IOException { |
| FSImageTransactionalStorageInspector inspector = |
| new FSImageTransactionalStorageInspector(); |
| storage.inspectStorageDirs(inspector); |
| |
| long minImageTxId = getImageTxIdToRetain(inspector); |
| purgeCheckpointsOlderThan(inspector, minImageTxId); |
| // If fsimage_N is the image we want to keep, then we need to keep |
| // all txns > N. We can remove anything < N+1, since fsimage_N |
| // reflects the state up to and including N. |
| editLog.purgeLogsOlderThan(minImageTxId + 1); |
| } |
| |
| private void purgeCheckpointsOlderThan( |
| FSImageTransactionalStorageInspector inspector, |
| long minTxId) { |
| for (FSImageFile image : inspector.getFoundImages()) { |
| if (image.getCheckpointTxId() < minTxId) { |
| LOG.info("Purging old image " + image); |
| purger.purgeImage(image); |
| } |
| } |
| } |
| |
| /** |
| * @param inspector inspector that has already inspected all storage dirs |
| * @return the transaction ID corresponding to the oldest checkpoint |
| * that should be retained. |
| */ |
| private long getImageTxIdToRetain(FSImageTransactionalStorageInspector inspector) { |
| |
| List<FSImageFile> images = inspector.getFoundImages(); |
| TreeSet<Long> imageTxIds = Sets.newTreeSet(); |
| for (FSImageFile image : images) { |
| imageTxIds.add(image.getCheckpointTxId()); |
| } |
| |
| List<Long> imageTxIdsList = Lists.newArrayList(imageTxIds); |
| if (imageTxIdsList.isEmpty()) { |
| return 0; |
| } |
| |
| Collections.reverse(imageTxIdsList); |
| int toRetain = Math.min(numCheckpointsToRetain, imageTxIdsList.size()); |
| long minTxId = imageTxIdsList.get(toRetain - 1); |
| LOG.info("Going to retain " + toRetain + " images with txid >= " + |
| minTxId); |
| return minTxId; |
| } |
| |
| /** |
| * Interface responsible for disposing of old checkpoints and edit logs. |
| */ |
| static interface StoragePurger { |
| void purgeLog(EditLogFile log); |
| void purgeImage(FSImageFile image); |
| } |
| |
| static class DeletionStoragePurger implements StoragePurger { |
| @Override |
| public void purgeLog(EditLogFile log) { |
| deleteOrWarn(log.getFile()); |
| } |
| |
| @Override |
| public void purgeImage(FSImageFile image) { |
| deleteOrWarn(image.getFile()); |
| deleteOrWarn(MD5FileUtils.getDigestFileForFile(image.getFile())); |
| } |
| |
| private static void deleteOrWarn(File file) { |
| if (!file.delete()) { |
| // It's OK if we fail to delete something -- we'll catch it |
| // next time we swing through this directory. |
| LOG.warn("Could not delete " + file); |
| } |
| } |
| } |
| } |