/** * 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 com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.PermissionStatus; import org.apache.hadoop.hdfs.protocol.Block; import org.apache.hadoop.hdfs.server.namenode.BlocksMap.BlockInfo; import org.apache.zookeeper.Op; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; /** * Utility class for generating random edit log sequences. Inspired by * the logic used in {@link TestEditLogBenchmark} * </p> * Current log ops used: OP_ADD, OP_CLOSE, OP_DELETE, * OP_SET_REPLICATION, OP_SET_GENSTAMP, OP_MKDIR, * OP_RENAME, OP_SET_OWNER, OP_SET_QUOTA, OP_TIMES, * OP_SET_PERMISSIONS, OP_CONCAT_DELETE */ public class LogOpGenerator { private static final Log LOG = LogFactory.getLog(LogOpGenerator.class); private static final int OPS_PER_FILE = 12; private final int numFiles; private final int blocksPerFile; private final ArrayList<FSEditLogOp> possibleOps; private final int numOpsPossible; private final Random random; private INodeId inodeId; /** * Instantiates pseudo-random edit log sequence generator. * @param numFiles Total number of distinct files * @param blocksPerFile Total number of distinct blocks per file */ public LogOpGenerator(int numFiles, int blocksPerFile) { this.numFiles = numFiles; this.blocksPerFile = blocksPerFile; numOpsPossible = numFiles * OPS_PER_FILE; possibleOps = Lists.newArrayListWithExpectedSize(numOpsPossible); random = new Random(); inodeId = new INodeId(); init(); } /** * Prefills the list of possible operations. */ private void init() { LOG.info("--- Generating " + numOpsPossible + " log operations! ---"); Random rng = new Random(); for (int i = 0; i < numFiles; i++) { PermissionStatus p = new PermissionStatus("hadoop", "hadoop", new FsPermission((short)0777)); INodeFileUnderConstruction inode = new INodeFileUnderConstruction(inodeId.nextValue(), p, (short) 3, 64, 0, "", "", null); for (int b = 0; b < blocksPerFile; b++) { Block block = new Block(b); BlockInfo bi = new BlockInfo(block, 3); bi.setChecksum(rng.nextInt(Integer.MAX_VALUE) + 1); try { inode.storage.addBlock(bi); } catch (IOException ioe) { LOG.error("Cannot add block", ioe); } } FsPermission perm = new FsPermission((short) 0); String name = "/filename-" + i; possibleOps.addAll(Arrays.asList(newOpenFile(name, inode), newCloseFile(name, inode), newDelete(name, 0), newSetReplication(name, (short)3), newGenerationStamp(i), newMkDir(name, inode), newRename(name, name, i), newSetOwner(name, "hadoop", "hadop"), newSetQuota(name, 1, 1), newTimes(name, 0 , 0), newSetPermissions(name, perm), newConcat(name, new String[] { name, name, name}, i), newMerge(name, name, "xor", new int[]{1, 1, 1}, i))); } LOG.info("--- Created " + numOpsPossible + " log operations! ---"); } /** * Generate contiguous (monotonically increasing) transactions chosen at * random. * @param firstTxId First transaction id to include * @param lastTxId Last transaction id to include * @return Log segment of log operations with transaction ids firstTxId to * lastTxId (inclusive) chosen using a pseudo-random generator */ public List<FSEditLogOp> generateContiguousSegment(int firstTxId, int lastTxId) { List<FSEditLogOp> contiguousOps = Lists.newArrayListWithExpectedSize(lastTxId - firstTxId + 1); for (int i = firstTxId; i <= lastTxId; i++) { FSEditLogOp randomOp = possibleOps.get(random.nextInt(numOpsPossible)); randomOp.setTransactionId(i); contiguousOps.add(randomOp); } return contiguousOps; } public static FSEditLogOp newOpenFile(String path, INodeFileUnderConstruction newNode) { FSEditLogOp.AddOp op = FSEditLogOp.AddOp.getUniqueInstance(); op.set(newNode.getId(), path, newNode.getReplication(), newNode.getModificationTime(), newNode.getAccessTime(), newNode.getPreferredBlockSize(), newNode.getBlocks(), newNode.getPermissionStatus(), newNode.getClientName(), newNode.getClientMachine()); return op; } public static FSEditLogOp newCloseFile(String path, INodeFile newNode) { FSEditLogOp.CloseOp op = FSEditLogOp.CloseOp.getUniqueInstance(); op.set(newNode.getId(), path, newNode.getReplication(), newNode.getModificationTime(), newNode.getAccessTime(), newNode.getPreferredBlockSize(), newNode.getBlocks(), newNode.getPermissionStatus(), null, null); return op; } public static FSEditLogOp newDelete(String src, long timestamp){ FSEditLogOp.DeleteOp op = FSEditLogOp.DeleteOp.getUniqueInstance(); op.set(src, timestamp); return op; } public static FSEditLogOp newSetReplication(String src, short replication) { FSEditLogOp.SetReplicationOp op = FSEditLogOp.SetReplicationOp.getUniqueInstance(); op.set(src, replication); return op; } public static FSEditLogOp newGenerationStamp(long genstamp) { FSEditLogOp.SetGenstampOp op = FSEditLogOp.SetGenstampOp.getUniqueInstance(); op.set(genstamp); return op; } // Helper methods for generating various edit log operations public static FSEditLogOp newMkDir(String path, INode newNode) { FSEditLogOp.MkdirOp op = FSEditLogOp.MkdirOp.getUniqueInstance(); op.set(newNode.getId(), path, newNode.getModificationTime(), newNode.getPermissionStatus()); return op; } public static FSEditLogOp getMkDirInstance(String path) { FSEditLogOp.MkdirOp op = FSEditLogOp.MkdirOp.getUniqueInstance(); op.set(INodeId.GRANDFATHER_INODE_ID, path, 0, new PermissionStatus("hadoop", "hadoop", new FsPermission( (short) 0777))); return op; } public static FSEditLogOp newRename(String src, String dst, long timestamp) { FSEditLogOp.RenameOp op = FSEditLogOp.RenameOp.getUniqueInstance(); op.set(src, dst, timestamp); return op; } public static FSEditLogOp newSetOwner(String src, String username, String groupname) { FSEditLogOp.SetOwnerOp op = FSEditLogOp.SetOwnerOp.getUniqueInstance(); op.set(src, username, groupname); return op; } public static FSEditLogOp newSetQuota(String src, long nsQuota, long dsQuota) { FSEditLogOp.SetQuotaOp op = FSEditLogOp.SetQuotaOp.getUniqueInstance(); op.set(src, nsQuota, dsQuota); return op; } public static FSEditLogOp newTimes(String src, long mtime, long atime) { FSEditLogOp.TimesOp op = FSEditLogOp.TimesOp.getUniqueInstance(); op.set(src, mtime, atime); return op; } public static FSEditLogOp newSetPermissions(String src, FsPermission permissions) { FSEditLogOp.SetPermissionsOp op = FSEditLogOp.SetPermissionsOp.getUniqueInstance(); op.set(src, permissions); return op; } public static FSEditLogOp newConcat(String trg, String [] srcs, long timestamp) { FSEditLogOp.ConcatDeleteOp op = FSEditLogOp.ConcatDeleteOp.getUniqueInstance(); op.set(trg, srcs, timestamp); return op; } public static FSEditLogOp newMerge(String parity, String source, String codecId, int[] checksums, long timestamp) { FSEditLogOp.MergeOp op = FSEditLogOp.MergeOp.getUniqueInstance(); op.set(parity, source, codecId, checksums, timestamp); return op; } }