View Javadoc

1   /**
2    *
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
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
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.
18   */
19  package org.apache.hadoop.hbase.wal;
20  
21  import static org.apache.hadoop.hbase.wal.DefaultWALProvider.META_WAL_PROVIDER_ID;
22  import static org.apache.hadoop.hbase.wal.DefaultWALProvider.WAL_FILE_NAME_DELIMITER;
23  
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  import java.util.concurrent.locks.Lock;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.classification.InterfaceAudience;
36  // imports for classes still in regionserver.wal
37  import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
38  import org.apache.hadoop.hbase.util.Bytes;
39  import org.apache.hadoop.hbase.util.IdReadWriteLock;
40  
41  /**
42   * A WAL Provider that returns a WAL per group of regions.
43   *
44   * This provider follows the decorator pattern and mainly holds the logic for WAL grouping.
45   * WAL creation/roll/close is delegated to {@link #DELEGATE_PROVIDER}
46   *
47   * Region grouping is handled via {@link RegionGroupingStrategy} and can be configured via the
48   * property "hbase.wal.regiongrouping.strategy". Current strategy choices are
49   * <ul>
50   *   <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
51   *                                  "bounded".</li>
52   *   <li><em>identity</em> : each region belongs to its own group.</li>
53   *   <li><em>bounded</em> : bounded number of groups and region evenly assigned to each group.</li>
54   * </ul>
55   * Optionally, a FQCN to a custom implementation may be given.
56   */
57  @InterfaceAudience.Private
58  public class RegionGroupingProvider implements WALProvider {
59    private static final Log LOG = LogFactory.getLog(RegionGroupingProvider.class);
60  
61    /**
62     * Map identifiers to a group number.
63     */
64    public static interface RegionGroupingStrategy {
65  
66      /**
67       * Given an identifier and a namespace, pick a group.
68       */
69      String group(final byte[] identifier, byte[] namespace);
70      void init(Configuration config, String providerId);
71    }
72  
73    /**
74     * Maps between configuration names for strategies and implementation classes.
75     */
76    static enum Strategies {
77      defaultStrategy(BoundedGroupingStrategy.class),
78      identity(IdentityGroupingStrategy.class),
79      bounded(BoundedGroupingStrategy.class),
80      namespace(NamespaceGroupingStrategy.class);
81  
82      final Class<? extends RegionGroupingStrategy> clazz;
83      Strategies(Class<? extends RegionGroupingStrategy> clazz) {
84        this.clazz = clazz;
85      }
86    }
87  
88    /**
89     * instantiate a strategy from a config property.
90     * requires conf to have already been set (as well as anything the provider might need to read).
91     */
92    RegionGroupingStrategy getStrategy(final Configuration conf, final String key,
93        final String defaultValue) throws IOException {
94      Class<? extends RegionGroupingStrategy> clazz;
95      try {
96        clazz = Strategies.valueOf(conf.get(key, defaultValue)).clazz;
97      } catch (IllegalArgumentException exception) {
98        // Fall back to them specifying a class name
99        // Note that the passed default class shouldn't actually be used, since the above only fails
100       // when there is a config value present.
101       clazz = conf.getClass(key, IdentityGroupingStrategy.class, RegionGroupingStrategy.class);
102     }
103     LOG.info("Instantiating RegionGroupingStrategy of type " + clazz);
104     try {
105       final RegionGroupingStrategy result = clazz.newInstance();
106       result.init(conf, providerId);
107       return result;
108     } catch (InstantiationException exception) {
109       LOG.error("couldn't set up region grouping strategy, check config key " +
110           REGION_GROUPING_STRATEGY);
111       LOG.debug("Exception details for failure to load region grouping strategy.", exception);
112       throw new IOException("couldn't set up region grouping strategy", exception);
113     } catch (IllegalAccessException exception) {
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.", exception);
117       throw new IOException("couldn't set up region grouping strategy", exception);
118     }
119   }
120 
121   public static final String REGION_GROUPING_STRATEGY = "hbase.wal.regiongrouping.strategy";
122   public static final String DEFAULT_REGION_GROUPING_STRATEGY = Strategies.defaultStrategy.name();
123 
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();
128 
129   private static final String META_WAL_GROUP_NAME = "meta";
130 
131   /** A group-provider mapping, make sure one-one rather than many-one mapping */
132   private final ConcurrentMap<String, WALProvider> cached = new ConcurrentHashMap<>();
133 
134   private final IdReadWriteLock createLock = new IdReadWriteLock();
135 
136   private RegionGroupingStrategy strategy = null;
137   private WALFactory factory = null;
138   private List<WALActionsListener> listeners = null;
139   private String providerId = null;
140   private Class<? extends WALProvider> providerClass;
141 
142   @Override
143   public void init(final WALFactory factory, final Configuration conf,
144       final List<WALActionsListener> listeners, final String providerId) throws IOException {
145     if (null != strategy) {
146       throw new IllegalStateException("WALProvider.init should only be called once.");
147     }
148     this.factory = factory;
149     this.listeners = null == listeners ? null : Collections.unmodifiableList(listeners);
150     StringBuilder sb = new StringBuilder().append(factory.factoryId);
151     if (providerId != null) {
152       if (providerId.startsWith(WAL_FILE_NAME_DELIMITER)) {
153         sb.append(providerId);
154       } else {
155         sb.append(WAL_FILE_NAME_DELIMITER).append(providerId);
156       }
157     }
158     this.providerId = sb.toString();
159     this.strategy = getStrategy(conf, REGION_GROUPING_STRATEGY, DEFAULT_REGION_GROUPING_STRATEGY);
160     this.providerClass = factory.getProviderClass(DELEGATE_PROVIDER, DEFAULT_DELEGATE_PROVIDER);
161     if (providerClass.equals(this.getClass())) {
162       LOG.warn("delegate provider not support multiwal, falling back to defaultProvider.");
163       providerClass = WALFactory.Providers.defaultProvider.clazz;
164     }
165   }
166 
167   private WALProvider createProvider(String group) throws IOException {
168     if (META_WAL_PROVIDER_ID.equals(providerId)) {
169       return factory.createProvider(providerClass, listeners, META_WAL_PROVIDER_ID);
170     } else {
171       return factory.createProvider(providerClass, listeners, group);
172     }
173   }
174 
175   @Override
176   public List<WAL> getWALs() throws IOException {
177     List<WAL> wals = new ArrayList<WAL>();
178     for (WALProvider provider : cached.values()) {
179       wals.addAll(provider.getWALs());
180     }
181     return wals;
182   }
183 
184   private WAL getWAL(final String group) throws IOException {
185     WALProvider provider = cached.get(group);
186     if (provider == null) {
187       Lock lock = createLock.getLock(group.hashCode()).writeLock();
188       lock.lock();
189       try {
190         provider = cached.get(group);
191         if (provider == null) {
192           provider = createProvider(group);
193           cached.put(group, provider);
194         }
195       } finally {
196         lock.unlock();
197       }
198     }
199     return provider.getWAL(null, null);
200   }
201 
202   @Override
203   public WAL getWAL(final byte[] identifier, byte[] namespace) throws IOException {
204     final String group;
205     if (META_WAL_PROVIDER_ID.equals(this.providerId)) {
206       group = META_WAL_GROUP_NAME;
207     } else {
208       group = strategy.group(identifier, namespace);
209     }
210     return getWAL(group);
211   }
212 
213   @Override
214   public void shutdown() throws IOException {
215     // save the last exception and rethrow
216     IOException failure = null;
217     for (WALProvider provider: cached.values()) {
218       try {
219         provider.shutdown();
220       } catch (IOException e) {
221         LOG.error("Problem shutting down wal provider '" + provider + "': " + e.getMessage());
222         if (LOG.isDebugEnabled()) {
223           LOG.debug("Details of problem shutting down wal provider '" + provider + "'", e);
224         }
225         failure = e;
226       }
227     }
228     if (failure != null) {
229       throw failure;
230     }
231   }
232 
233   @Override
234   public void close() throws IOException {
235     // save the last exception and rethrow
236     IOException failure = null;
237     for (WALProvider provider : cached.values()) {
238       try {
239         provider.close();
240       } catch (IOException e) {
241         LOG.error("Problem closing wal provider '" + provider + "': " + e.getMessage());
242         if (LOG.isDebugEnabled()) {
243           LOG.debug("Details of problem closing wal provider '" + provider + "'", e);
244         }
245         failure = e;
246       }
247     }
248     if (failure != null) {
249       throw failure;
250     }
251   }
252 
253   static class IdentityGroupingStrategy implements RegionGroupingStrategy {
254     @Override
255     public void init(Configuration config, String providerId) {}
256     @Override
257     public String group(final byte[] identifier, final byte[] namespace) {
258       return "identity-" + Bytes.toString(identifier);
259     }
260   }
261 
262   @Override
263   public long getNumLogFiles() {
264     long numLogFiles = 0;
265     for (WALProvider provider : cached.values()) {
266       numLogFiles += provider.getNumLogFiles();
267     }
268     return numLogFiles;
269   }
270 
271   @Override
272   public long getLogFileSize() {
273     long logFileSize = 0;
274     for (WALProvider provider : cached.values()) {
275       logFileSize += provider.getLogFileSize();
276     }
277     return logFileSize;
278   }
279 }