HBASE-26921 Rewrite the counting cells part in TestMultiVersions (#4316)
[hbase.git] / hbase-server / src / main / java / org / apache / hadoop / hbase / wal / RegionGroupingProvider.java
blob20d043b6ae26d486d1f81a5ad9aaef0324c4c462
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;
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;
45 /**
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
53 * <ul>
54 * <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
55 * "bounded".</li>
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>
58 * </ul>
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);
65 /**
66 * Map identifiers to a group number.
68 public static interface RegionGroupingStrategy {
69 String GROUP_NAME_DELIMITER = ".";
71 /**
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);
78 /**
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) {
89 this.clazz = clazz;
93 /**
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;
100 try {
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);
109 try {
110 final RegionGroupingStrategy result = clazz.getDeclaredConstructor().newInstance();
111 result.init(conf, providerId);
112 return result;
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
127 .name();
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;
144 @Override
145 public void init(WALFactory factory, Configuration conf, String providerId, Abortable abortable)
146 throws IOException {
147 if (null != strategy) {
148 throw new IllegalStateException("WALProvider.init should only be called once.");
150 this.conf = conf;
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;
157 } else {
158 StringBuilder sb = new StringBuilder().append(factory.factoryId);
159 if (providerId != null) {
160 if (providerId.startsWith(WAL_FILE_NAME_DELIMITER)) {
161 sb.append(providerId);
162 } else {
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());
181 return provider;
184 @Override
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);
193 try {
194 provider = cached.get(group);
195 if (provider == null) {
196 provider = createProvider(group);
197 listeners.forEach(provider::addWALActionsListener);
198 cached.put(group, provider);
200 } finally {
201 lock.unlock();
204 return provider.getWAL(null);
207 @Override
208 public WAL getWAL(RegionInfo region) throws IOException {
209 String group;
210 if (META_WAL_PROVIDER_ID.equals(this.providerId)) {
211 group = META_WAL_GROUP_NAME;
212 } else {
213 byte[] id;
214 byte[] namespace;
215 if (region != null) {
216 id = region.getEncodedNameAsBytes();
217 namespace = region.getTable().getNamespace();
218 } else {
219 id = HConstants.EMPTY_BYTE_ARRAY;
220 namespace = null;
222 group = strategy.group(id, namespace);
224 return getWAL(group);
227 @Override
228 public void shutdown() throws IOException {
229 // save the last exception and rethrow
230 IOException failure = null;
231 for (WALProvider provider: cached.values()) {
232 try {
233 provider.shutdown();
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);
239 failure = e;
242 if (failure != null) {
243 throw failure;
247 @Override
248 public void close() throws IOException {
249 // save the last exception and rethrow
250 IOException failure = null;
251 for (WALProvider provider : cached.values()) {
252 try {
253 provider.close();
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);
259 failure = e;
262 if (failure != null) {
263 throw failure;
267 static class IdentityGroupingStrategy implements RegionGroupingStrategy {
268 @Override
269 public void init(Configuration config, String providerId) {}
270 @Override
271 public String group(final byte[] identifier, final byte[] namespace) {
272 return Bytes.toString(identifier);
276 @Override
277 public long getNumLogFiles() {
278 long numLogFiles = 0;
279 for (WALProvider provider : cached.values()) {
280 numLogFiles += provider.getNumLogFiles();
282 return numLogFiles;
285 @Override
286 public long getLogFileSize() {
287 long logFileSize = 0;
288 for (WALProvider provider : cached.values()) {
289 logFileSize += provider.getLogFileSize();
291 return logFileSize;
294 @Override
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);