blob: 621da70735df2da62bfdb0b05df845a006a6fe10 [file] [log] [blame]
/*
* 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.ozone.container.ozoneimpl;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.scm.container.common.helpers
.StorageContainerException;
import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.common.Storage;
import org.apache.hadoop.ozone.container.common.helpers.BlockData;
import org.apache.hadoop.ozone.container.common.helpers.ChunkInfo;
import org.apache.hadoop.ozone.container.common.helpers.ContainerUtils;
import org.apache.hadoop.ozone.container.common.impl.ContainerData;
import org.apache.hadoop.ozone.container.common.impl.ContainerSet;
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
import org.apache.hadoop.ozone.container.common.volume.VolumeSet;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueBlockIterator;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainer;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
import org.apache.hadoop.ozone.container.common.impl.ContainerDataYaml;
import org.apache.hadoop.ozone.container.keyvalue.helpers.BlockUtils;
import org.apache.hadoop.ozone.container.keyvalue.helpers.KeyValueContainerUtil;
import org.apache.hadoop.hdds.utils.MetadataKeyFilters;
import org.apache.hadoop.ozone.container.common.utils.ReferenceCountedDB;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.List;
/**
* Class used to read .container files from Volume and build container map.
*
* Layout of the container directory on disk is as follows:
*
* <p>../hdds/VERSION
* <p>{@literal ../hdds/<<scmUuid>>/current/<<containerDir>>/<<containerID
* >/metadata/<<containerID>>.container}
* <p>{@literal ../hdds/<<scmUuid>>/current/<<containerDir>>/<<containerID
* >/<<dataPath>>}
* <p>
* Some ContainerTypes will have extra metadata other than the .container
* file. For example, KeyValueContainer will have a .db file. This .db file
* will also be stored in the metadata folder along with the .container file.
* <p>
* {@literal ../hdds/<<scmUuid>>/current/<<containerDir>>/<<KVcontainerID
* >/metadata/<<KVcontainerID>>.db}
* <p>
* Note that the {@literal <<dataPath>>} is dependent on the ContainerType.
* For KeyValueContainers, the data is stored in a "chunks" folder. As such,
* the {@literal <<dataPath>>} layout for KeyValueContainers is:
* <p>{@literal ../hdds/<<scmUuid>>/current/<<containerDir>>/<<KVcontainerID
* >/chunks/<<chunksFile>>}
*
*/
public class ContainerReader implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(
ContainerReader.class);
private HddsVolume hddsVolume;
private final ContainerSet containerSet;
private final OzoneConfiguration config;
private final File hddsVolumeDir;
private final VolumeSet volumeSet;
ContainerReader(VolumeSet volSet, HddsVolume volume, ContainerSet cset,
OzoneConfiguration conf) {
Preconditions.checkNotNull(volume);
this.hddsVolume = volume;
this.hddsVolumeDir = hddsVolume.getHddsRootDir();
this.containerSet = cset;
this.config = conf;
this.volumeSet = volSet;
}
@Override
public void run() {
try {
readVolume(hddsVolumeDir);
} catch (RuntimeException ex) {
LOG.error("Caught a Run time exception during reading container files" +
" from Volume {} {}", hddsVolumeDir, ex);
}
}
public void readVolume(File hddsVolumeRootDir) {
Preconditions.checkNotNull(hddsVolumeRootDir, "hddsVolumeRootDir" +
"cannot be null");
//filtering scm directory
File[] scmDir = hddsVolumeRootDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
if (scmDir == null) {
LOG.error("IO error for the volume {}, skipped loading",
hddsVolumeRootDir);
volumeSet.failVolume(hddsVolumeRootDir.getPath());
return;
}
if (scmDir.length > 1) {
LOG.error("Volume {} is in Inconsistent state", hddsVolumeRootDir);
volumeSet.failVolume(hddsVolumeRootDir.getPath());
return;
}
for (File scmLoc : scmDir) {
File currentDir = new File(scmLoc, Storage.STORAGE_DIR_CURRENT);
File[] containerTopDirs = currentDir.listFiles();
if (containerTopDirs != null) {
for (File containerTopDir : containerTopDirs) {
if (containerTopDir.isDirectory()) {
File[] containerDirs = containerTopDir.listFiles();
if (containerDirs != null) {
for (File containerDir : containerDirs) {
File containerFile = ContainerUtils.getContainerFile(
containerDir);
long containerID = ContainerUtils.getContainerID(containerDir);
if (containerFile.exists()) {
verifyContainerFile(containerID, containerFile);
} else {
LOG.error("Missing .container file for ContainerID: {}",
containerDir.getName());
}
}
}
}
}
}
}
}
private void verifyContainerFile(long containerID, File containerFile) {
try {
ContainerData containerData = ContainerDataYaml.readContainerFile(
containerFile);
if (containerID != containerData.getContainerID()) {
LOG.error("Invalid ContainerID in file {}. " +
"Skipping loading of this container.", containerFile);
return;
}
verifyAndFixupContainerData(containerData);
} catch (IOException ex) {
LOG.error("Failed to parse ContainerFile for ContainerID: {}",
containerID, ex);
}
}
/**
* verify ContainerData loaded from disk and fix-up stale members.
* Specifically blockCommitSequenceId, delete related metadata
* and bytesUsed
* @param containerData
* @throws IOException
*/
public void verifyAndFixupContainerData(ContainerData containerData)
throws IOException {
switch (containerData.getContainerType()) {
case KeyValueContainer:
if (containerData instanceof KeyValueContainerData) {
KeyValueContainerData kvContainerData = (KeyValueContainerData)
containerData;
containerData.setVolume(hddsVolume);
KeyValueContainerUtil.parseKVContainerData(kvContainerData, config);
KeyValueContainer kvContainer = new KeyValueContainer(
kvContainerData, config);
try(ReferenceCountedDB containerDB = BlockUtils.getDB(kvContainerData,
config)) {
MetadataKeyFilters.KeyPrefixFilter filter =
new MetadataKeyFilters.KeyPrefixFilter()
.addFilter(OzoneConsts.DELETING_KEY_PREFIX);
int numPendingDeletionBlocks =
containerDB.getStore().getSequentialRangeKVs(null,
Integer.MAX_VALUE, filter)
.size();
kvContainerData.incrPendingDeletionBlocks(numPendingDeletionBlocks);
byte[] delTxnId = containerDB.getStore().get(
DFSUtil.string2Bytes(OzoneConsts.DELETE_TRANSACTION_KEY_PREFIX));
if (delTxnId != null) {
kvContainerData
.updateDeleteTransactionId(Longs.fromByteArray(delTxnId));
}
// sets the BlockCommitSequenceId.
byte[] bcsId = containerDB.getStore().get(DFSUtil.string2Bytes(
OzoneConsts.BLOCK_COMMIT_SEQUENCE_ID_PREFIX));
if (bcsId != null) {
kvContainerData
.updateBlockCommitSequenceId(Longs.fromByteArray(bcsId));
}
if (kvContainer.getContainerState()
== ContainerProtos.ContainerDataProto.State.OPEN) {
// commitSpace for Open Containers relies on usedBytes
initializeUsedBytes(kvContainer);
}
containerSet.addContainer(kvContainer);
}
} else {
throw new StorageContainerException("Container File is corrupted. " +
"ContainerType is KeyValueContainer but cast to " +
"KeyValueContainerData failed. ",
ContainerProtos.Result.CONTAINER_METADATA_ERROR);
}
break;
default:
throw new StorageContainerException("Unrecognized ContainerType " +
containerData.getContainerType(),
ContainerProtos.Result.UNKNOWN_CONTAINER_TYPE);
}
}
private void initializeUsedBytes(KeyValueContainer container)
throws IOException {
try (KeyValueBlockIterator blockIter = new KeyValueBlockIterator(
container.getContainerData().getContainerID(),
new File(container.getContainerData().getContainerPath()))) {
long usedBytes = 0;
while (blockIter.hasNext()) {
BlockData block = blockIter.nextBlock();
long blockLen = 0;
List<ContainerProtos.ChunkInfo> chunkInfoList = block.getChunks();
for (ContainerProtos.ChunkInfo chunk : chunkInfoList) {
ChunkInfo info = ChunkInfo.getFromProtoBuf(chunk);
blockLen += info.getLen();
}
usedBytes += blockLen;
}
container.getContainerData().setBytesUsed(usedBytes);
}
}
}