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      String GROUP_NAME_DELIMITER = ".";
66  
67      /**
68       * Given an identifier and a namespace, pick a group.
69       */
70      String group(final byte[] identifier, byte[] namespace);
71      void init(Configuration config, String providerId);
72    }
73  
74    /**
75     * Maps between configuration names for strategies and implementation classes.
76     */
77    static enum Strategies {
78      defaultStrategy(BoundedGroupingStrategy.class),
79      identity(IdentityGroupingStrategy.class),
80      bounded(BoundedGroupingStrategy.class),
81      namespace(NamespaceGroupingStrategy.class);
82  
83      final Class<? extends RegionGroupingStrategy> clazz;
84      Strategies(Class<? extends RegionGroupingStrategy> clazz) {
85        this.clazz = clazz;
86      }
87    }
88  
89    /**
90     * instantiate a strategy from a config property.
91     * requires conf to have already been set (as well as anything the provider might need to read).
92     */
93    RegionGroupingStrategy getStrategy(final Configuration conf, final String key,
94        final String defaultValue) throws IOException {
95      Class<? extends RegionGroupingStrategy> clazz;
96      try {
97        clazz = Strategies.valueOf(conf.get(key, defaultValue)).clazz;
98      } catch (IllegalArgumentException exception) {
99        // Fall back to them specifying a class name
100       // Note that the passed default class shouldn't actually be used, since the above only fails
101       // when there is a config value present.
102       clazz = conf.getClass(key, IdentityGroupingStrategy.class, RegionGroupingStrategy.class);
103     }
104     LOG.info("Instantiating RegionGroupingStrategy of type " + clazz);
105     try {
106       final RegionGroupingStrategy result = clazz.newInstance();
107       result.init(conf, providerId);
108       return result;
109     } catch (InstantiationException exception) {
110       LOG.error("couldn't set up region grouping strategy, check config key " +
111           REGION_GROUPING_STRATEGY);
112       LOG.debug("Exception details for failure to load region grouping strategy.", exception);
113       throw new IOException("couldn't set up region grouping strategy", exception);
114     } catch (IllegalAccessException exception) {
115       LOG.error("couldn't set up region grouping strategy, check config key " +
116           REGION_GROUPING_STRATEGY);
117       LOG.debug("Exception details for failure to load region grouping strategy.", exception);
118       throw new IOException("couldn't set up region grouping strategy", exception);
119     }
120   }
121 
122   public static final String REGION_GROUPING_STRATEGY = "hbase.wal.regiongrouping.strategy";
123   public static final String DEFAULT_REGION_GROUPING_STRATEGY = Strategies.defaultStrategy.name();
124 
125   /** delegate provider for WAL creation/roll/close */
126   public static final String DELEGATE_PROVIDER = "hbase.wal.regiongrouping.delegate.provider";
127   public static final String DEFAULT_DELEGATE_PROVIDER = WALFactory.Providers.defaultProvider
128       .name();
129 
130   private static final String META_WAL_GROUP_NAME = "meta";
131 
132   /** A group-provider mapping, make sure one-one rather than many-one mapping */
133   private final ConcurrentMap<String, WALProvider> cached = new ConcurrentHashMap<>();
134 
135   private final IdReadWriteLock createLock = new IdReadWriteLock();
136 
137   private RegionGroupingStrategy strategy = null;
138   private WALFactory factory = null;
139   private List<WALActionsListener> listeners = null;
140   private String providerId = null;
141   private Class<? extends WALProvider> providerClass;
142 
143   @Override
144   public void init(final WALFactory factory, final Configuration conf,
145       final List<WALActionsListener> listeners, final String providerId) throws IOException {
146     if (null != strategy) {
147       throw new IllegalStateException("WALProvider.init should only be called once.");
148     }
149     this.factory = factory;
150     this.listeners = null == listeners ? null : Collections.unmodifiableList(listeners);
151     StringBuilder sb = new StringBuilder().append(factory.factoryId);
152     if (providerId != null) {
153       if (providerId.startsWith(WAL_FILE_NAME_DELIMITER)) {
154         sb.append(providerId);
155       } else {
156         sb.append(WAL_FILE_NAME_DELIMITER).append(providerId);
157       }
158     }
159     this.providerId = sb.toString();
160     this.strategy = getStrategy(conf, REGION_GROUPING_STRATEGY, DEFAULT_REGION_GROUPING_STRATEGY);
161     this.providerClass = factory.getProviderClass(DELEGATE_PROVIDER, DEFAULT_DELEGATE_PROVIDER);
162   }
163 
164   private WALProvider createProvider(String group) throws IOException {
165     if (META_WAL_PROVIDER_ID.equals(providerId)) {
166       return factory.createProvider(providerClass, listeners, META_WAL_PROVIDER_ID);
167     } else {
168       return factory.createProvider(providerClass, listeners, group);
169     }
170   }
171 
172   @Override
173   public List<WAL> getWALs() throws IOException {
174     List<WAL> wals = new ArrayList<WAL>();
175     for (WALProvider provider : cached.values()) {
176       wals.addAll(provider.getWALs());
177     }
178     return wals;
179   }
180 
181   private WAL getWAL(final String group) throws IOException {
182     WALProvider provider = cached.get(group);
183     if (provider == null) {
184       Lock lock = createLock.getLock(group.hashCode()).writeLock();
185       lock.lock();
186       try {
187         provider = cached.get(group);
188         if (provider == null) {
189           provider = createProvider(group);
190           cached.put(group, provider);
191         }
192       } finally {
193         lock.unlock();
194       }
195     }
196     return provider.getWAL(null, null);
197   }
198 
199   @Override
200   public WAL getWAL(final byte[] identifier, byte[] namespace) throws IOException {
201     final String group;
202     if (META_WAL_PROVIDER_ID.equals(this.providerId)) {
203       group = META_WAL_GROUP_NAME;
204     } else {
205       group = strategy.group(identifier, namespace);
206     }
207     return getWAL(group);
208   }
209 
210   @Override
211   public void shutdown() throws IOException {
212     // save the last exception and rethrow
213     IOException failure = null;
214     for (WALProvider provider: cached.values()) {
215       try {
216         provider.shutdown();
217       } catch (IOException e) {
218         LOG.error("Problem shutting down wal provider '" + provider + "': " + e.getMessage());
219         if (LOG.isDebugEnabled()) {
220           LOG.debug("Details of problem shutting down wal provider '" + provider + "'", e);
221         }
222         failure = e;
223       }
224     }
225     if (failure != null) {
226       throw failure;
227     }
228   }
229 
230   @Override
231   public void close() throws IOException {
232     // save the last exception and rethrow
233     IOException failure = null;
234     for (WALProvider provider : cached.values()) {
235       try {
236         provider.close();
237       } catch (IOException e) {
238         LOG.error("Problem closing wal provider '" + provider + "': " + e.getMessage());
239         if (LOG.isDebugEnabled()) {
240           LOG.debug("Details of problem closing wal provider '" + provider + "'", e);
241         }
242         failure = e;
243       }
244     }
245     if (failure != null) {
246       throw failure;
247     }
248   }
249 
250   static class IdentityGroupingStrategy implements RegionGroupingStrategy {
251     @Override
252     public void init(Configuration config, String providerId) {}
253     @Override
254     public String group(final byte[] identifier, final byte[] namespace) {
255       return Bytes.toString(identifier);
256     }
257   }
258 
259   @Override
260   public long getNumLogFiles() {
261     long numLogFiles = 0;
262     for (WALProvider provider : cached.values()) {
263       numLogFiles += provider.getNumLogFiles();
264     }
265     return numLogFiles;
266   }
267 
268   @Override
269   public long getLogFileSize() {
270     long logFileSize = 0;
271     for (WALProvider provider : cached.values()) {
272       logFileSize += provider.getLogFileSize();
273     }
274     return logFileSize;
275   }
276 }