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.regionserver;
20  
21  import java.io.IOException;
22  import java.util.Comparator;
23  import java.util.List;
24  
25  import org.apache.commons.lang.ClassUtils;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.hbase.CellScanner;
30  import org.apache.hadoop.hbase.Coprocessor;
31  import org.apache.hadoop.hbase.CoprocessorEnvironment;
32  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
33  import org.apache.hadoop.hbase.MetaMutationAnnotation;
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
35  import org.apache.hadoop.hbase.classification.InterfaceStability;
36  import org.apache.hadoop.hbase.client.Mutation;
37  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
38  import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
39  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
40  import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
41  import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
42  import org.apache.hadoop.hbase.coprocessor.SingletonCoprocessorService;
43  
44  import org.apache.hadoop.hbase.ipc.RpcServer;
45  import org.apache.hadoop.hbase.metrics.MetricRegistry;
46  import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
47  import org.apache.hadoop.hbase.security.User;
48  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.WALEntry;
49  
50  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
51  @InterfaceStability.Evolving
52  public class RegionServerCoprocessorHost extends
53      CoprocessorHost<RegionServerCoprocessorHost.RegionServerEnvironment> {
54  
55    private static final Log LOG = LogFactory.getLog(RegionServerCoprocessorHost.class);
56  
57    private RegionServerServices rsServices;
58  
59    public RegionServerCoprocessorHost(RegionServerServices rsServices,
60        Configuration conf) {
61      super(rsServices);
62      this.rsServices = rsServices;
63      this.conf = conf;
64      // Log the state of coprocessor loading here; should appear only once or
65      // twice in the daemon log, depending on HBase version, because there is
66      // only one RegionServerCoprocessorHost instance in the RS process
67      boolean coprocessorsEnabled = conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
68        DEFAULT_COPROCESSORS_ENABLED);
69      boolean tableCoprocessorsEnabled = conf.getBoolean(USER_COPROCESSORS_ENABLED_CONF_KEY,
70        DEFAULT_USER_COPROCESSORS_ENABLED);
71      LOG.info("System coprocessor loading is " + (coprocessorsEnabled ? "enabled" : "disabled"));
72      LOG.info("Table coprocessor loading is " +
73        ((coprocessorsEnabled && tableCoprocessorsEnabled) ? "enabled" : "disabled"));
74      loadSystemCoprocessors(conf, REGIONSERVER_COPROCESSOR_CONF_KEY);
75    }
76  
77    @Override
78    public RegionServerEnvironment createEnvironment(Class<?> implClass,
79        Coprocessor instance, int priority, int sequence, Configuration conf) {
80      return new RegionServerEnvironment(implClass, instance, priority,
81        sequence, conf, this.rsServices);
82    }
83  
84    public void preStop(String message) throws IOException {
85      // While stopping the region server all coprocessors method should be executed first then the
86      // coprocessor should be cleaned up.
87      execShutdown(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
88        @Override
89        public void call(RegionServerObserver oserver,
90            ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
91          oserver.preStopRegionServer(ctx);
92        }
93        @Override
94        public void postEnvCall(RegionServerEnvironment env) {
95          // invoke coprocessor stop method
96          shutdown(env);
97        }
98      });
99    }
100 
101   public boolean preMerge(final HRegion regionA, final HRegion regionB, final User user)
102       throws IOException {
103     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
104       @Override
105       public void call(RegionServerObserver oserver,
106           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
107         oserver.preMerge(ctx, regionA, regionB);
108       }
109     });
110   }
111 
112   public void postMerge(final HRegion regionA, final HRegion regionB, final HRegion mergedRegion,
113       final User user) throws IOException {
114     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
115       @Override
116       public void call(RegionServerObserver oserver,
117           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
118         oserver.postMerge(ctx, regionA, regionB, mergedRegion);
119       }
120     });
121   }
122 
123   public boolean preMergeCommit(final HRegion regionA, final HRegion regionB,
124       final @MetaMutationAnnotation List<Mutation> metaEntries, final User user)
125       throws IOException {
126     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
127       @Override
128       public void call(RegionServerObserver oserver,
129           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
130         oserver.preMergeCommit(ctx, regionA, regionB, metaEntries);
131       }
132     });
133   }
134 
135   public void postMergeCommit(final HRegion regionA, final HRegion regionB,
136       final HRegion mergedRegion, final User user) throws IOException {
137     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
138       @Override
139       public void call(RegionServerObserver oserver,
140           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
141         oserver.postMergeCommit(ctx, regionA, regionB, mergedRegion);
142       }
143     });
144   }
145 
146   public void preRollBackMerge(final HRegion regionA, final HRegion regionB, final User user)
147       throws IOException {
148     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
149       @Override
150       public void call(RegionServerObserver oserver,
151           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
152         oserver.preRollBackMerge(ctx, regionA, regionB);
153       }
154     });
155   }
156 
157   public void postRollBackMerge(final HRegion regionA, final HRegion regionB, final User user)
158       throws IOException {
159     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
160       @Override
161       public void call(RegionServerObserver oserver,
162           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
163         oserver.postRollBackMerge(ctx, regionA, regionB);
164       }
165     });
166   }
167 
168   public void preRollWALWriterRequest() throws IOException {
169     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
170       @Override
171       public void call(RegionServerObserver oserver,
172           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
173         oserver.preRollWALWriterRequest(ctx);
174       }
175     });
176   }
177 
178   public void postRollWALWriterRequest() throws IOException {
179     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
180       @Override
181       public void call(RegionServerObserver oserver,
182           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
183         oserver.postRollWALWriterRequest(ctx);
184       }
185     });
186   }
187 
188   public void preReplicateLogEntries(final List<WALEntry> entries, final CellScanner cells)
189       throws IOException {
190     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
191       @Override
192       public void call(RegionServerObserver oserver,
193           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
194         oserver.preReplicateLogEntries(ctx, entries, cells);
195       }
196     });
197   }
198 
199   public void postReplicateLogEntries(final List<WALEntry> entries, final CellScanner cells)
200       throws IOException {
201     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
202       @Override
203       public void call(RegionServerObserver oserver,
204           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
205         oserver.postReplicateLogEntries(ctx, entries, cells);
206       }
207     });
208   }
209 
210   public ReplicationEndpoint postCreateReplicationEndPoint(final ReplicationEndpoint endpoint)
211       throws IOException {
212     return execOperationWithResult(endpoint, coprocessors.isEmpty() ? null
213         : new CoprocessOperationWithResult<ReplicationEndpoint>() {
214           @Override
215           public void call(RegionServerObserver oserver,
216               ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
217             setResult(oserver.postCreateReplicationEndPoint(ctx, getResult()));
218           }
219         });
220   }
221 
222   private <T> T execOperationWithResult(final T defaultValue,
223       final CoprocessOperationWithResult<T> ctx) throws IOException {
224     if (ctx == null)
225       return defaultValue;
226     ctx.setResult(defaultValue);
227     execOperation(ctx);
228     return ctx.getResult();
229   }
230 
231   private static abstract class CoprocessorOperation
232       extends ObserverContext<RegionServerCoprocessorEnvironment> {
233     public CoprocessorOperation() {
234       this(RpcServer.getRequestUser());
235     }
236 
237     public CoprocessorOperation(User user) {
238       super(user);
239     }
240 
241     public abstract void call(RegionServerObserver oserver,
242         ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException;
243 
244     public void postEnvCall(RegionServerEnvironment env) {
245     }
246   }
247 
248   private static abstract class CoprocessOperationWithResult<T> extends CoprocessorOperation {
249     private T result = null;
250 
251     public void setResult(final T result) {
252       this.result = result;
253     }
254 
255     public T getResult() {
256       return this.result;
257     }
258   }
259 
260   private boolean execOperation(final CoprocessorOperation ctx) throws IOException {
261     if (ctx == null) return false;
262     boolean bypass = false;
263     List<RegionServerEnvironment> envs = coprocessors.get();
264     for (int i = 0; i < envs.size(); i++) {
265       RegionServerEnvironment env = envs.get(i);
266       if (env.getInstance() instanceof RegionServerObserver) {
267         ctx.prepare(env);
268         Thread currentThread = Thread.currentThread();
269         ClassLoader cl = currentThread.getContextClassLoader();
270         try {
271           currentThread.setContextClassLoader(env.getClassLoader());
272           ctx.call((RegionServerObserver)env.getInstance(), ctx);
273         } catch (Throwable e) {
274           handleCoprocessorThrowable(env, e);
275         } finally {
276           currentThread.setContextClassLoader(cl);
277         }
278         bypass |= ctx.shouldBypass();
279         if (ctx.shouldComplete()) {
280           break;
281         }
282       }
283       ctx.postEnvCall(env);
284     }
285     return bypass;
286   }
287 
288   /**
289    * RegionServer coprocessor classes can be configured in any order, based on that priority is set
290    * and chained in a sorted order. For preStop(), coprocessor methods are invoked in call() and
291    * environment is shutdown in postEnvCall(). <br>
292    * Need to execute all coprocessor methods first then postEnvCall(), otherwise some coprocessors
293    * may remain shutdown if any exception occurs during next coprocessor execution which prevent
294    * RegionServer stop. (Refer:
295    * <a href="https://issues.apache.org/jira/browse/HBASE-16663">HBASE-16663</a>
296    * @param ctx CoprocessorOperation
297    * @return true if bypaas coprocessor execution, false if not.
298    * @throws IOException
299    */
300   private boolean execShutdown(final CoprocessorOperation ctx) throws IOException {
301     if (ctx == null) return false;
302     boolean bypass = false;
303     List<RegionServerEnvironment> envs = coprocessors.get();
304     int envsSize = envs.size();
305     // Iterate the coprocessors and execute CoprocessorOperation's call()
306     for (int i = 0; i < envsSize; i++) {
307       RegionServerEnvironment env = envs.get(i);
308       if (env.getInstance() instanceof RegionServerObserver) {
309         ctx.prepare(env);
310         Thread currentThread = Thread.currentThread();
311         ClassLoader cl = currentThread.getContextClassLoader();
312         try {
313           currentThread.setContextClassLoader(env.getClassLoader());
314           ctx.call((RegionServerObserver) env.getInstance(), ctx);
315         } catch (Throwable e) {
316           handleCoprocessorThrowable(env, e);
317         } finally {
318           currentThread.setContextClassLoader(cl);
319         }
320         bypass |= ctx.shouldBypass();
321         if (ctx.shouldComplete()) {
322           break;
323         }
324       }
325     }
326 
327     // Iterate the coprocessors and execute CoprocessorOperation's postEnvCall()
328     for (int i = 0; i < envsSize; i++) {
329       RegionServerEnvironment env = envs.get(i);
330       ctx.postEnvCall(env);
331     }
332     return bypass;
333   }
334 
335   /**
336    * Coprocessor environment extension providing access to region server
337    * related services.
338    */
339   static class RegionServerEnvironment extends CoprocessorHost.Environment
340       implements RegionServerCoprocessorEnvironment {
341     private final RegionServerServices regionServerServices;
342     private final MetricRegistry metricRegistry;
343 
344     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="BC_UNCONFIRMED_CAST",
345         justification="Intentional; FB has trouble detecting isAssignableFrom")
346     public RegionServerEnvironment(final Class<?> implClass,
347         final Coprocessor impl, final int priority, final int seq,
348         final Configuration conf, final RegionServerServices services) {
349       super(impl, priority, seq, conf);
350       this.regionServerServices = services;
351       for (Object itf : ClassUtils.getAllInterfaces(implClass)) {
352         Class<?> c = (Class<?>) itf;
353         if (SingletonCoprocessorService.class.isAssignableFrom(c)) {// FindBugs: BC_UNCONFIRMED_CAST
354           this.regionServerServices.registerService(
355             ((SingletonCoprocessorService) impl).getService());
356           break;
357         }
358       }
359       this.metricRegistry =
360           MetricsCoprocessor.createRegistryForRSCoprocessor(implClass.getName());
361     }
362 
363     @Override
364     public RegionServerServices getRegionServerServices() {
365       return regionServerServices;
366     }
367 
368     @Override
369     public MetricRegistry getMetricRegistryForRegionServer() {
370       return metricRegistry;
371     }
372 
373     @Override
374     protected void shutdown() {
375       super.shutdown();
376       MetricsCoprocessor.removeRegistry(metricRegistry);
377     }
378   }
379 
380   /**
381    * Environment priority comparator. Coprocessors are chained in sorted
382    * order.
383    */
384   static class EnvironmentPriorityComparator implements
385       Comparator<CoprocessorEnvironment> {
386     @Override
387     public int compare(final CoprocessorEnvironment env1,
388         final CoprocessorEnvironment env2) {
389       if (env1.getPriority() < env2.getPriority()) {
390         return -1;
391       } else if (env1.getPriority() > env2.getPriority()) {
392         return 1;
393       }
394       if (env1.getLoadSequence() < env2.getLoadSequence()) {
395         return -1;
396       } else if (env1.getLoadSequence() > env2.getLoadSequence()) {
397         return 1;
398       }
399       return 0;
400     }
401   }
402 }