HBASE-21843 RegionGroupingProvider breaks the meta wal file name pattern which may...
[hbase.git] / hbase-server / src / main / java / org / apache / hadoop / hbase / wal / RegionGroupingProvider.java
blobd6a36421cbdaff03661bdcf733acde7896874b9e
1 /**
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 package org.apache.hadoop.hbase.wal;
21 import static org.apache.hadoop.hbase.wal.AbstractFSWALProvider.META_WAL_PROVIDER_ID;
22 import static org.apache.hadoop.hbase.wal.AbstractFSWALProvider.WAL_FILE_NAME_DELIMITER;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.locks.Lock;
30 import java.util.stream.Collectors;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.client.RegionInfo;
34 import org.apache.hadoop.hbase.regionserver.wal.MetricsWAL;
35 // imports for classes still in regionserver.wal
36 import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
37 import org.apache.hadoop.hbase.util.Bytes;
38 import org.apache.hadoop.hbase.util.KeyLocker;
39 import org.apache.yetus.audience.InterfaceAudience;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 /**
44 * A WAL Provider that returns a WAL per group of regions.
46 * This provider follows the decorator pattern and mainly holds the logic for WAL grouping.
47 * WAL creation/roll/close is delegated to {@link #DELEGATE_PROVIDER}
49 * Region grouping is handled via {@link RegionGroupingStrategy} and can be configured via the
50 * property "hbase.wal.regiongrouping.strategy". Current strategy choices are
51 * <ul>
52 * <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
53 * "bounded".</li>
54 * <li><em>identity</em> : each region belongs to its own group.</li>
55 * <li><em>bounded</em> : bounded number of groups and region evenly assigned to each group.</li>
56 * </ul>
57 * Optionally, a FQCN to a custom implementation may be given.
59 @InterfaceAudience.Private
60 public class RegionGroupingProvider implements WALProvider {
61 private static final Logger LOG = LoggerFactory.getLogger(RegionGroupingProvider.class);
63 /**
64 * Map identifiers to a group number.
66 public static interface RegionGroupingStrategy {
67 String GROUP_NAME_DELIMITER = ".";
69 /**
70 * Given an identifier and a namespace, pick a group.
72 String group(final byte[] identifier, byte[] namespace);
73 void init(Configuration config, String providerId);
76 /**
77 * Maps between configuration names for strategies and implementation classes.
79 static enum Strategies {
80 defaultStrategy(BoundedGroupingStrategy.class),
81 identity(IdentityGroupingStrategy.class),
82 bounded(BoundedGroupingStrategy.class),
83 namespace(NamespaceGroupingStrategy.class);
85 final Class<? extends RegionGroupingStrategy> clazz;
86 Strategies(Class<? extends RegionGroupingStrategy> clazz) {
87 this.clazz = clazz;
91 /**
92 * instantiate a strategy from a config property.
93 * requires conf to have already been set (as well as anything the provider might need to read).
95 RegionGroupingStrategy getStrategy(final Configuration conf, final String key,
96 final String defaultValue) throws IOException {
97 Class<? extends RegionGroupingStrategy> clazz;
98 try {
99 clazz = Strategies.valueOf(conf.get(key, defaultValue)).clazz;
100 } catch (IllegalArgumentException exception) {
101 // Fall back to them specifying a class name
102 // Note that the passed default class shouldn't actually be used, since the above only fails
103 // when there is a config value present.
104 clazz = conf.getClass(key, IdentityGroupingStrategy.class, RegionGroupingStrategy.class);
106 LOG.info("Instantiating RegionGroupingStrategy of type " + clazz);
107 try {
108 final RegionGroupingStrategy result = clazz.getDeclaredConstructor().newInstance();
109 result.init(conf, providerId);
110 return result;
111 } catch (Exception e) {
112 LOG.error("couldn't set up region grouping strategy, check config key " +
113 REGION_GROUPING_STRATEGY);
114 LOG.debug("Exception details for failure to load region grouping strategy.", e);
115 throw new IOException("couldn't set up region grouping strategy", e);
119 public static final String REGION_GROUPING_STRATEGY = "hbase.wal.regiongrouping.strategy";
120 public static final String DEFAULT_REGION_GROUPING_STRATEGY = Strategies.defaultStrategy.name();
122 /** delegate provider for WAL creation/roll/close */
123 public static final String DELEGATE_PROVIDER = "hbase.wal.regiongrouping.delegate.provider";
124 public static final String DEFAULT_DELEGATE_PROVIDER = WALFactory.Providers.defaultProvider
125 .name();
127 private static final String META_WAL_GROUP_NAME = "meta";
129 /** A group-provider mapping, make sure one-one rather than many-one mapping */
130 private final ConcurrentMap<String, WALProvider> cached = new ConcurrentHashMap<>();
132 private final KeyLocker<String> createLock = new KeyLocker<>();
134 private RegionGroupingStrategy strategy;
135 private WALFactory factory;
136 private Configuration conf;
137 private List<WALActionsListener> listeners = new ArrayList<>();
138 private String providerId;
139 private Class<? extends WALProvider> providerClass;
141 @Override
142 public void init(WALFactory factory, Configuration conf, String providerId) throws IOException {
143 if (null != strategy) {
144 throw new IllegalStateException("WALProvider.init should only be called once.");
146 this.conf = conf;
147 this.factory = factory;
149 if (META_WAL_PROVIDER_ID.equals(providerId)) {
150 // do not change the provider id if it is for meta
151 this.providerId = providerId;
152 } else {
153 StringBuilder sb = new StringBuilder().append(factory.factoryId);
154 if (providerId != null) {
155 if (providerId.startsWith(WAL_FILE_NAME_DELIMITER)) {
156 sb.append(providerId);
157 } else {
158 sb.append(WAL_FILE_NAME_DELIMITER).append(providerId);
161 this.providerId = sb.toString();
163 this.strategy = getStrategy(conf, REGION_GROUPING_STRATEGY, DEFAULT_REGION_GROUPING_STRATEGY);
164 this.providerClass = factory.getProviderClass(DELEGATE_PROVIDER, DEFAULT_DELEGATE_PROVIDER);
167 private WALProvider createProvider(String group) throws IOException {
168 WALProvider provider = WALFactory.createProvider(providerClass);
169 provider.init(factory, conf,
170 META_WAL_PROVIDER_ID.equals(providerId) ? META_WAL_PROVIDER_ID : group);
171 provider.addWALActionsListener(new MetricsWAL());
172 return provider;
175 @Override
176 public List<WAL> getWALs() {
177 return cached.values().stream().flatMap(p -> p.getWALs().stream()).collect(Collectors.toList());
180 private WAL getWAL(String group) throws IOException {
181 WALProvider provider = cached.get(group);
182 if (provider == null) {
183 Lock lock = createLock.acquireLock(group);
184 try {
185 provider = cached.get(group);
186 if (provider == null) {
187 provider = createProvider(group);
188 listeners.forEach(provider::addWALActionsListener);
189 cached.put(group, provider);
191 } finally {
192 lock.unlock();
195 return provider.getWAL(null);
198 @Override
199 public WAL getWAL(RegionInfo region) throws IOException {
200 String group;
201 if (META_WAL_PROVIDER_ID.equals(this.providerId)) {
202 group = META_WAL_GROUP_NAME;
203 } else {
204 byte[] id;
205 byte[] namespace;
206 if (region != null) {
207 id = region.getEncodedNameAsBytes();
208 namespace = region.getTable().getNamespace();
209 } else {
210 id = HConstants.EMPTY_BYTE_ARRAY;
211 namespace = null;
213 group = strategy.group(id, namespace);
215 return getWAL(group);
218 @Override
219 public void shutdown() throws IOException {
220 // save the last exception and rethrow
221 IOException failure = null;
222 for (WALProvider provider: cached.values()) {
223 try {
224 provider.shutdown();
225 } catch (IOException e) {
226 LOG.error("Problem shutting down wal provider '" + provider + "': " + e.getMessage());
227 if (LOG.isDebugEnabled()) {
228 LOG.debug("Details of problem shutting down wal provider '" + provider + "'", e);
230 failure = e;
233 if (failure != null) {
234 throw failure;
238 @Override
239 public void close() throws IOException {
240 // save the last exception and rethrow
241 IOException failure = null;
242 for (WALProvider provider : cached.values()) {
243 try {
244 provider.close();
245 } catch (IOException e) {
246 LOG.error("Problem closing wal provider '" + provider + "': " + e.getMessage());
247 if (LOG.isDebugEnabled()) {
248 LOG.debug("Details of problem closing wal provider '" + provider + "'", e);
250 failure = e;
253 if (failure != null) {
254 throw failure;
258 static class IdentityGroupingStrategy implements RegionGroupingStrategy {
259 @Override
260 public void init(Configuration config, String providerId) {}
261 @Override
262 public String group(final byte[] identifier, final byte[] namespace) {
263 return Bytes.toString(identifier);
267 @Override
268 public long getNumLogFiles() {
269 long numLogFiles = 0;
270 for (WALProvider provider : cached.values()) {
271 numLogFiles += provider.getNumLogFiles();
273 return numLogFiles;
276 @Override
277 public long getLogFileSize() {
278 long logFileSize = 0;
279 for (WALProvider provider : cached.values()) {
280 logFileSize += provider.getLogFileSize();
282 return logFileSize;
285 @Override
286 public void addWALActionsListener(WALActionsListener listener) {
287 // Notice that there is an assumption that this method must be called before the getWAL above,
288 // so we can make sure there is no sub WALProvider yet, so we only add the listener to our
289 // listeners list without calling addWALActionListener for each WALProvider. Although it is no
290 // hurt to execute an extra loop to call addWALActionListener for each WALProvider, but if the
291 // extra code actually works, then we will have other big problems. So leave it as is.
292 listeners.add(listener);