From 8c57d114cd124679753d70569283d13365b51d01 Mon Sep 17 00:00:00 2001 From: Sean Busbey Date: Thu, 18 Dec 2014 09:43:38 -0600 Subject: [PATCH] HBASE-12719 test provider to remove just the hdfs interactions from fshlog. * Adds a provider that takes a config to determine if we actually do syncs, appends, and file rolls. - required relaxing some members of FSHLog from private to protected. * Fixes DisabledWALProvider so it can be used as hbase.wal.provider in the wal perf eval tool. --- .../hadoop/hbase/regionserver/wal/FSHLog.java | 5 +- .../hadoop/hbase/wal/DisabledWALProvider.java | 5 +- .../apache/hadoop/hbase/wal/IOTestProvider.java | 233 ++++++++++++++++++++ 3 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/wal/IOTestProvider.java diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java index 248b71a..1fad93d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/FSHLog.java @@ -203,7 +203,8 @@ public class FSHLog implements WAL { /** * file system instance */ - private final FileSystem fs; + protected final FileSystem fs; + /** * WAL directory, where all WAL files would be placed. */ @@ -238,7 +239,7 @@ public class FSHLog implements WAL { /** * conf object */ - private final Configuration conf; + protected final Configuration conf; /** Listeners that are called on WAL events. */ private final List listeners = new CopyOnWriteArrayList(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/wal/DisabledWALProvider.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/wal/DisabledWALProvider.java index f571166..5bffea5 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/wal/DisabledWALProvider.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/wal/DisabledWALProvider.java @@ -56,10 +56,13 @@ class DisabledWALProvider implements WALProvider { @Override public void init(final WALFactory factory, final Configuration conf, - final List listeners, final String providerId) throws IOException { + final List listeners, String providerId) throws IOException { if (null != disabled) { throw new IllegalStateException("WALProvider.init should only be called once."); } + if (null == providerId) { + providerId = "defaultDisabled"; + } disabled = new DisabledWAL(new Path(FSUtils.getRootDir(conf), providerId), conf, null); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/wal/IOTestProvider.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/wal/IOTestProvider.java new file mode 100644 index 0000000..d2581a1 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/wal/IOTestProvider.java @@ -0,0 +1,233 @@ +/** + * + * 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.hbase.wal; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.util.FSUtils; +import org.apache.hadoop.hbase.wal.WAL.Entry; + +import static org.apache.hadoop.hbase.wal.DefaultWALProvider.DEFAULT_PROVIDER_ID; +import static org.apache.hadoop.hbase.wal.DefaultWALProvider.META_WAL_PROVIDER_ID; +import static org.apache.hadoop.hbase.wal.DefaultWALProvider.WAL_FILE_NAME_DELIMITER; + + +// imports for things that haven't moved from regionserver.wal yet. +import org.apache.hadoop.hbase.regionserver.wal.FSHLog; +import org.apache.hadoop.hbase.regionserver.wal.ProtobufLogWriter; +import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener; + +/** + * A WAL Provider that returns a single thread safe WAL that optionally can skip parts of our + * normal interactions with HDFS. + * + * This implementation picks a directory in HDFS based on the same mechanisms as the + * {@link DefaultWALProvider}. Users can configure how much interaction + * we have with HDFS with the configuration property "hbase.wal.iotestprovider.operations". + * The value should be a comma separated list of allowed operations: + *
    + *
  • append : edits will be written to the underlying filesystem + *
  • sync : wal syncs will result in hflush calls + *
  • fileroll : roll requests will result in creating a new file on the underlying + * filesystem. + *
+ * Additionally, the special cases "all" and "none" are recognized. + * If ommited, the value defaults to "all." + * Behavior is undefined if "all" or "none" are paired with additional values. Behavior is also + * undefined if values not listed above are included. + * + * Only those operations listed will occur between the returned WAL and HDFS. All others + * will be no-ops. + * + * Note that in the case of allowing "append" operations but not allowing "fileroll", the returned + * WAL will just keep writing to the same file. This won't avoid all costs associated with file + * management over time, becaue the data set size may result in additional HDFS block allocations. + * + */ +@InterfaceAudience.Private +public class IOTestProvider implements WALProvider { + private static final Log LOG = LogFactory.getLog(IOTestProvider.class); + + private static final String ALLOWED_OPERATIONS = "hbase.wal.iotestprovider.operations"; + private enum AllowedOperations { + all, + append, + sync, + fileroll, + none; + } + + private FSHLog log = null; + + /** + * @param factory factory that made us, identity used for FS layout. may not be null + * @param conf may not be null + * @param listeners may be null + * @param providerId differentiate between providers from one facotry, used for FS layout. may be + * null + */ + @Override + public void init(final WALFactory factory, final Configuration conf, + final List listeners, String providerId) throws IOException { + if (null != log) { + throw new IllegalStateException("WALProvider.init should only be called once."); + } + if (null == providerId) { + providerId = DEFAULT_PROVIDER_ID; + } + final String logPrefix = factory.factoryId + WAL_FILE_NAME_DELIMITER + providerId; + log = new IOTestWAL(FileSystem.get(conf), FSUtils.getRootDir(conf), + DefaultWALProvider.getWALDirectoryName(factory.factoryId), + HConstants.HREGION_OLDLOGDIR_NAME, conf, listeners, + true, logPrefix, META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : null); + } + + @Override + public WAL getWAL(final byte[] identifier) throws IOException { + return log; + } + + @Override + public void close() throws IOException { + log.close(); + } + + @Override + public void shutdown() throws IOException { + log.shutdown(); + } + + private static class IOTestWAL extends FSHLog { + + private final boolean doFileRolls; + + // Used to differntiate between roll calls before and after we finish construction. + private final boolean initialized; + + /** + * Create an edit log at the given dir location. + * + * You should never have to load an existing log. If there is a log at + * startup, it should have already been processed and deleted by the time the + * WAL object is started up. + * + * @param fs filesystem handle + * @param rootDir path to where logs and oldlogs + * @param logDir dir where wals are stored + * @param archiveDir dir where wals are archived + * @param conf configuration to use + * @param listeners Listeners on WAL events. Listeners passed here will + * be registered before we do anything else; e.g. the + * Constructor {@link #rollWriter()}. + * @param failIfWALExists If true IOException will be thrown if files related to this wal + * already exist. + * @param prefix should always be hostname and port in distributed env and + * it will be URL encoded before being used. + * If prefix is null, "wal" will be used + * @param suffix will be url encoded. null is treated as empty. non-empty must start with + * {@link DefaultWALProvider#WAL_FILE_NAME_DELIMITER} + * @throws IOException + */ + public IOTestWAL(final FileSystem fs, final Path rootDir, final String logDir, + final String archiveDir, final Configuration conf, + final List listeners, + final boolean failIfWALExists, final String prefix, final String suffix) + throws IOException { + super(fs, rootDir, logDir, archiveDir, conf, listeners, failIfWALExists, prefix, suffix); + Collection operations = conf.getStringCollection(ALLOWED_OPERATIONS); + doFileRolls = operations.isEmpty() || operations.contains(AllowedOperations.all.name()) || + operations.contains(AllowedOperations.fileroll.name()); + initialized = true; + LOG.info("Initialized with file rolling " + (doFileRolls ? "enabled" : "disabled")); + } + + private Writer noRollsWriter; + + // creatWriterInstance is where the new pipeline is set up for doing file rolls + // if we are skipping it, just keep returning the same writer. + @Override + protected Writer createWriterInstance(final Path path) throws IOException { + // we get called from the FSHLog constructor (!); always roll in this case since + // we don't know yet if we're supposed to generally roll and + // we need an initial file in the case of doing appends but no rolls. + if (!initialized || doFileRolls) { + LOG.info("creating new writer instance."); + final ProtobufLogWriter writer = new IOTestWriter(); + writer.init(fs, path, conf, false); + if (!initialized) { + LOG.info("storing initial writer instance in case file rolling isn't allowed."); + noRollsWriter = writer; + } + return writer; + } else { + LOG.info("WAL rolling disabled, returning the first writer."); + // Initial assignment happens during the constructor call, so there ought not be + // a race for first assignment. + return noRollsWriter; + } + } + } + + /** + * Presumes init will be called by a single thread prior to any access of other methods. + */ + private static class IOTestWriter extends ProtobufLogWriter { + private boolean doAppends; + private boolean doSyncs; + + @Override + public void init(FileSystem fs, Path path, Configuration conf, boolean overwritable) throws IOException { + Collection operations = conf.getStringCollection(ALLOWED_OPERATIONS); + if (operations.isEmpty() || operations.contains(AllowedOperations.all.name())) { + doAppends = doSyncs = true; + } else if (operations.contains(AllowedOperations.none.name())) { + doAppends = doSyncs = false; + } else { + doAppends = operations.contains(AllowedOperations.append.name()); + doSyncs = operations.contains(AllowedOperations.sync.name()); + } + LOG.info("IOTestWriter initialized with appends " + (doAppends ? "enabled" : "disabled") + + " and syncs " + (doSyncs ? "enabled" : "disabled")); + super.init(fs, path, conf, overwritable); + } + + @Override + public void append(Entry entry) throws IOException { + if (doAppends) { + super.append(entry); + } + } + + @Override + public void sync() throws IOException { + if (doSyncs) { + super.sync(); + } + } + } +} -- 1.7.10.2 (Apple Git-33)