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
;
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
52 * <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
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>
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);
64 * Map identifiers to a group number.
66 public static interface RegionGroupingStrategy
{
67 String GROUP_NAME_DELIMITER
= ".";
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
);
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
) {
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
;
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
);
108 final RegionGroupingStrategy result
= clazz
.getDeclaredConstructor().newInstance();
109 result
.init(conf
, providerId
);
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
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
;
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.");
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
;
153 StringBuilder sb
= new StringBuilder().append(factory
.factoryId
);
154 if (providerId
!= null) {
155 if (providerId
.startsWith(WAL_FILE_NAME_DELIMITER
)) {
156 sb
.append(providerId
);
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());
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
);
185 provider
= cached
.get(group
);
186 if (provider
== null) {
187 provider
= createProvider(group
);
188 listeners
.forEach(provider
::addWALActionsListener
);
189 cached
.put(group
, provider
);
195 return provider
.getWAL(null);
199 public WAL
getWAL(RegionInfo region
) throws IOException
{
201 if (META_WAL_PROVIDER_ID
.equals(this.providerId
)) {
202 group
= META_WAL_GROUP_NAME
;
206 if (region
!= null) {
207 id
= region
.getEncodedNameAsBytes();
208 namespace
= region
.getTable().getNamespace();
210 id
= HConstants
.EMPTY_BYTE_ARRAY
;
213 group
= strategy
.group(id
, namespace
);
215 return getWAL(group
);
219 public void shutdown() throws IOException
{
220 // save the last exception and rethrow
221 IOException failure
= null;
222 for (WALProvider provider
: cached
.values()) {
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
);
233 if (failure
!= null) {
239 public void close() throws IOException
{
240 // save the last exception and rethrow
241 IOException failure
= null;
242 for (WALProvider provider
: cached
.values()) {
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
);
253 if (failure
!= null) {
258 static class IdentityGroupingStrategy
implements RegionGroupingStrategy
{
260 public void init(Configuration config
, String providerId
) {}
262 public String
group(final byte[] identifier
, final byte[] namespace
) {
263 return Bytes
.toString(identifier
);
268 public long getNumLogFiles() {
269 long numLogFiles
= 0;
270 for (WALProvider provider
: cached
.values()) {
271 numLogFiles
+= provider
.getNumLogFiles();
277 public long getLogFileSize() {
278 long logFileSize
= 0;
279 for (WALProvider provider
: cached
.values()) {
280 logFileSize
+= provider
.getLogFileSize();
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
);