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.Collections;
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  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
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.IdReadWriteLock;
39  
40  /**
41   * A WAL Provider that returns a WAL per group of regions.
42   *
43   * This provider follows the decorator pattern and mainly holds the logic for WAL grouping.
44   * WAL creation/roll/close is delegated to {@link #DELEGATE_PROVIDER}
45   *
46   * Region grouping is handled via {@link RegionGroupingStrategy} and can be configured via the
47   * property "hbase.wal.regiongrouping.strategy". Current strategy choices are
48   * <ul>
49   *   <li><em>defaultStrategy</em> : Whatever strategy this version of HBase picks. currently
50   *                                  "bounded".</li>
51   *   <li><em>identity</em> : each region belongs to its own group.</li>
52   *   <li><em>bounded</em> : bounded number of groups and region evenly assigned to each group.</li>
53   * </ul>
54   * Optionally, a FQCN to a custom implementation may be given.
55   */
56  @InterfaceAudience.Private
57  public class RegionGroupingProvider implements WALProvider {
58    private static final Log LOG = LogFactory.getLog(RegionGroupingProvider.class);
59  
60    /**
61     * Map identifiers to a group number.
62     */
63    public static interface RegionGroupingStrategy {
64      String GROUP_NAME_DELIMITER = ".";
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 */
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   }
162 
163   private WALProvider createProvider(String group) throws IOException {
164     if (META_WAL_PROVIDER_ID.equals(providerId)) {
165       return factory.createProvider(providerClass, listeners, META_WAL_PROVIDER_ID);
166     } else {
167       return factory.createProvider(providerClass, listeners, group);
168     }
169   }
170 
171   private WAL getWAL(final String group) throws IOException {
172     WALProvider provider = cached.get(group);
173     if (provider == null) {
174       Lock lock = createLock.getLock(group.hashCode()).writeLock();
175       lock.lock();
176       try {
177         provider = cached.get(group);
178         if (provider == null) {
179           provider = createProvider(group);
180           cached.put(group, provider);
181         }
182       } finally {
183         lock.unlock();
184       }
185     }
186     return provider.getWAL(null, null);
187   }
188 
189   @Override
190   public WAL getWAL(final byte[] identifier, byte[] namespace) throws IOException {
191     final String group;
192     if (META_WAL_PROVIDER_ID.equals(this.providerId)) {
193       group = META_WAL_GROUP_NAME;
194     } else {
195       group = strategy.group(identifier, namespace);
196     }
197     return getWAL(group);
198   }
199 
200   @Override
201   public void shutdown() throws IOException {
202     // save the last exception and rethrow
203     IOException failure = null;
204     for (WALProvider provider: cached.values()) {
205       try {
206         provider.shutdown();
207       } catch (IOException e) {
208         LOG.error("Problem shutting down wal provider '" + provider + "': " + e.getMessage());
209         if (LOG.isDebugEnabled()) {
210           LOG.debug("Details of problem shutting down wal provider '" + provider + "'", e);
211         }
212         failure = e;
213       }
214     }
215     if (failure != null) {
216       throw failure;
217     }
218   }
219 
220   @Override
221   public void close() throws IOException {
222     // save the last exception and rethrow
223     IOException failure = null;
224     for (WALProvider provider : cached.values()) {
225       try {
226         provider.close();
227       } catch (IOException e) {
228         LOG.error("Problem closing wal provider '" + provider + "': " + e.getMessage());
229         if (LOG.isDebugEnabled()) {
230           LOG.debug("Details of problem closing wal provider '" + provider + "'", e);
231         }
232         failure = e;
233       }
234     }
235     if (failure != null) {
236       throw failure;
237     }
238   }
239 
240   static class IdentityGroupingStrategy implements RegionGroupingStrategy {
241     @Override
242     public void init(Configuration config, String providerId) {}
243     @Override
244     public String group(final byte[] identifier, final byte[] namespace) {
245       return Bytes.toString(identifier);
246     }
247   }
248 
249   @Override
250   public long getNumLogFiles() {
251     long numLogFiles = 0;
252     for (WALProvider provider : cached.values()) {
253       numLogFiles += provider.getNumLogFiles();
254     }
255     return numLogFiles;
256   }
257 
258   @Override
259   public long getLogFileSize() {
260     long logFileSize = 0;
261     for (WALProvider provider : cached.values()) {
262       logFileSize += provider.getLogFileSize();
263     }
264     return logFileSize;
265   }
266 }