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
;
32 import org
.apache
.hadoop
.conf
.Configuration
;
33 import org
.apache
.hadoop
.hbase
.Abortable
;
34 import org
.apache
.hadoop
.hbase
.HConstants
;
35 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
36 import org
.apache
.hadoop
.hbase
.regionserver
.wal
.MetricsWAL
;
37 // imports for classes still in regionserver.wal
38 import org
.apache
.hadoop
.hbase
.regionserver
.wal
.WALActionsListener
;
39 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
40 import org
.apache
.hadoop
.hbase
.util
.KeyLocker
;
41 import org
.apache
.yetus
.audience
.InterfaceAudience
;
42 import org
.slf4j
.Logger
;
43 import org
.slf4j
.LoggerFactory
;
46 * A WAL Provider that returns a WAL per group of regions.
48 * This provider follows the decorator pattern and mainly holds the logic for WAL grouping.
49 * WAL creation/roll/close is delegated to {@link #DELEGATE_PROVIDER}
51 * Region grouping is handled via {@link RegionGroupingStrategy} and can be configured via the
52 * property "hbase.wal.regiongrouping.strategy". Current strategy choices are
54 * <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
56 * <li><em>identity</em> : each region belongs to its own group.</li>
57 * <li><em>bounded</em> : bounded number of groups and region evenly assigned to each group.</li>
59 * Optionally, a FQCN to a custom implementation may be given.
61 @InterfaceAudience.Private
62 public class RegionGroupingProvider
implements WALProvider
{
63 private static final Logger LOG
= LoggerFactory
.getLogger(RegionGroupingProvider
.class);
66 * Map identifiers to a group number.
68 public static interface RegionGroupingStrategy
{
69 String GROUP_NAME_DELIMITER
= ".";
72 * Given an identifier and a namespace, pick a group.
74 String
group(final byte[] identifier
, byte[] namespace
);
75 void init(Configuration config
, String providerId
);
79 * Maps between configuration names for strategies and implementation classes.
81 static enum Strategies
{
82 defaultStrategy(BoundedGroupingStrategy
.class),
83 identity(IdentityGroupingStrategy
.class),
84 bounded(BoundedGroupingStrategy
.class),
85 namespace(NamespaceGroupingStrategy
.class);
87 final Class
<?
extends RegionGroupingStrategy
> clazz
;
88 Strategies(Class
<?
extends RegionGroupingStrategy
> clazz
) {
94 * instantiate a strategy from a config property.
95 * requires conf to have already been set (as well as anything the provider might need to read).
97 RegionGroupingStrategy
getStrategy(final Configuration conf
, final String key
,
98 final String defaultValue
) throws IOException
{
99 Class
<?
extends RegionGroupingStrategy
> clazz
;
101 clazz
= Strategies
.valueOf(conf
.get(key
, defaultValue
)).clazz
;
102 } catch (IllegalArgumentException exception
) {
103 // Fall back to them specifying a class name
104 // Note that the passed default class shouldn't actually be used, since the above only fails
105 // when there is a config value present.
106 clazz
= conf
.getClass(key
, IdentityGroupingStrategy
.class, RegionGroupingStrategy
.class);
108 LOG
.info("Instantiating RegionGroupingStrategy of type " + clazz
);
110 final RegionGroupingStrategy result
= clazz
.getDeclaredConstructor().newInstance();
111 result
.init(conf
, providerId
);
113 } catch (Exception e
) {
114 LOG
.error("couldn't set up region grouping strategy, check config key " +
115 REGION_GROUPING_STRATEGY
);
116 LOG
.debug("Exception details for failure to load region grouping strategy.", e
);
117 throw new IOException("couldn't set up region grouping strategy", e
);
121 public static final String REGION_GROUPING_STRATEGY
= "hbase.wal.regiongrouping.strategy";
122 public static final String DEFAULT_REGION_GROUPING_STRATEGY
= Strategies
.defaultStrategy
.name();
124 /** delegate provider for WAL creation/roll/close, but not support multiwal */
125 public static final String DELEGATE_PROVIDER
= "hbase.wal.regiongrouping.delegate.provider";
126 public static final String DEFAULT_DELEGATE_PROVIDER
= WALFactory
.Providers
.defaultProvider
129 private static final String META_WAL_GROUP_NAME
= "meta";
131 /** A group-provider mapping, make sure one-one rather than many-one mapping */
132 private final ConcurrentMap
<String
, WALProvider
> cached
= new ConcurrentHashMap
<>();
134 private final KeyLocker
<String
> createLock
= new KeyLocker
<>();
136 private RegionGroupingStrategy strategy
;
137 private WALFactory factory
;
138 private Configuration conf
;
139 private List
<WALActionsListener
> listeners
= new ArrayList
<>();
140 private String providerId
;
141 private Class
<?
extends WALProvider
> providerClass
;
142 private Abortable abortable
;
145 public void init(WALFactory factory
, Configuration conf
, String providerId
, Abortable abortable
)
147 if (null != strategy
) {
148 throw new IllegalStateException("WALProvider.init should only be called once.");
151 this.factory
= factory
;
152 this.abortable
= abortable
;
154 if (META_WAL_PROVIDER_ID
.equals(providerId
)) {
155 // do not change the provider id if it is for meta
156 this.providerId
= providerId
;
158 StringBuilder sb
= new StringBuilder().append(factory
.factoryId
);
159 if (providerId
!= null) {
160 if (providerId
.startsWith(WAL_FILE_NAME_DELIMITER
)) {
161 sb
.append(providerId
);
163 sb
.append(WAL_FILE_NAME_DELIMITER
).append(providerId
);
166 this.providerId
= sb
.toString();
168 this.strategy
= getStrategy(conf
, REGION_GROUPING_STRATEGY
, DEFAULT_REGION_GROUPING_STRATEGY
);
169 this.providerClass
= factory
.getProviderClass(DELEGATE_PROVIDER
, DEFAULT_DELEGATE_PROVIDER
);
170 if (providerClass
.equals(this.getClass())) {
171 LOG
.warn("delegate provider not support multiwal, falling back to defaultProvider.");
172 providerClass
= factory
.getDefaultProvider().clazz
;
176 private WALProvider
createProvider(String group
) throws IOException
{
177 WALProvider provider
= WALFactory
.createProvider(providerClass
);
178 provider
.init(factory
, conf
,
179 META_WAL_PROVIDER_ID
.equals(providerId
) ? META_WAL_PROVIDER_ID
: group
, this.abortable
);
180 provider
.addWALActionsListener(new MetricsWAL());
185 public List
<WAL
> getWALs() {
186 return cached
.values().stream().flatMap(p
-> p
.getWALs().stream()).collect(Collectors
.toList());
189 private WAL
getWAL(String group
) throws IOException
{
190 WALProvider provider
= cached
.get(group
);
191 if (provider
== null) {
192 Lock lock
= createLock
.acquireLock(group
);
194 provider
= cached
.get(group
);
195 if (provider
== null) {
196 provider
= createProvider(group
);
197 listeners
.forEach(provider
::addWALActionsListener
);
198 cached
.put(group
, provider
);
204 return provider
.getWAL(null);
208 public WAL
getWAL(RegionInfo region
) throws IOException
{
210 if (META_WAL_PROVIDER_ID
.equals(this.providerId
)) {
211 group
= META_WAL_GROUP_NAME
;
215 if (region
!= null) {
216 id
= region
.getEncodedNameAsBytes();
217 namespace
= region
.getTable().getNamespace();
219 id
= HConstants
.EMPTY_BYTE_ARRAY
;
222 group
= strategy
.group(id
, namespace
);
224 return getWAL(group
);
228 public void shutdown() throws IOException
{
229 // save the last exception and rethrow
230 IOException failure
= null;
231 for (WALProvider provider
: cached
.values()) {
234 } catch (IOException e
) {
235 LOG
.error("Problem shutting down wal provider '" + provider
+ "': " + e
.getMessage());
236 if (LOG
.isDebugEnabled()) {
237 LOG
.debug("Details of problem shutting down wal provider '" + provider
+ "'", e
);
242 if (failure
!= null) {
248 public void close() throws IOException
{
249 // save the last exception and rethrow
250 IOException failure
= null;
251 for (WALProvider provider
: cached
.values()) {
254 } catch (IOException e
) {
255 LOG
.error("Problem closing wal provider '" + provider
+ "': " + e
.getMessage());
256 if (LOG
.isDebugEnabled()) {
257 LOG
.debug("Details of problem closing wal provider '" + provider
+ "'", e
);
262 if (failure
!= null) {
267 static class IdentityGroupingStrategy
implements RegionGroupingStrategy
{
269 public void init(Configuration config
, String providerId
) {}
271 public String
group(final byte[] identifier
, final byte[] namespace
) {
272 return Bytes
.toString(identifier
);
277 public long getNumLogFiles() {
278 long numLogFiles
= 0;
279 for (WALProvider provider
: cached
.values()) {
280 numLogFiles
+= provider
.getNumLogFiles();
286 public long getLogFileSize() {
287 long logFileSize
= 0;
288 for (WALProvider provider
: cached
.values()) {
289 logFileSize
+= provider
.getLogFileSize();
295 public void addWALActionsListener(WALActionsListener listener
) {
296 // Notice that there is an assumption that this method must be called before the getWAL above,
297 // so we can make sure there is no sub WALProvider yet, so we only add the listener to our
298 // listeners list without calling addWALActionListener for each WALProvider. Although it is no
299 // hurt to execute an extra loop to call addWALActionListener for each WALProvider, but if the
300 // extra code actually works, then we will have other big problems. So leave it as is.
301 listeners
.add(listener
);