001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020
021package org.apache.hadoop.hbase.coprocessor.example;
022
023import java.io.IOException;
024import java.util.List;
025import java.util.Optional;
026import java.util.concurrent.ThreadLocalRandom;
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.CoprocessorEnvironment;
029import org.apache.hadoop.hbase.client.Get;
030import org.apache.hadoop.hbase.coprocessor.ObserverContext;
031import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
032import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
033import org.apache.hadoop.hbase.coprocessor.RegionObserver;
034import org.apache.hadoop.hbase.metrics.Counter;
035import org.apache.hadoop.hbase.metrics.MetricRegistry;
036import org.apache.hadoop.hbase.metrics.Timer;
037import org.apache.hadoop.hbase.regionserver.FlushLifeCycleTracker;
038import org.apache.hadoop.hbase.regionserver.Store;
039import org.apache.hadoop.hbase.regionserver.StoreFile;
040import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker;
041import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
042import org.apache.yetus.audience.InterfaceAudience;
043
044/**
045 * An example coprocessor that collects some metrics to demonstrate the usage of exporting custom
046 * metrics from the coprocessor.
047 * <p>
048 * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb,
049 * etc) as well as JMX output. You can view a snapshot of the metrics by going to the http web UI
050 * of the regionserver page, something like http://myregionserverhost:16030/jmx
051 * </p>
052 *
053 * @see ExampleMasterObserverWithMetrics
054 */
055@InterfaceAudience.Private
056public class ExampleRegionObserverWithMetrics implements RegionCoprocessor {
057
058  private Counter preGetCounter;
059  private Counter flushCounter;
060  private Counter filesCompactedCounter;
061  private Timer costlyOperationTimer;
062  private ExampleRegionObserver observer;
063
064  class ExampleRegionObserver implements RegionCoprocessor, RegionObserver {
065    @Override
066    public Optional<RegionObserver> getRegionObserver() {
067      return Optional.of(this);
068    }
069
070    @Override
071    public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
072        List<Cell> results) throws IOException {
073      // Increment the Counter whenever the coprocessor is called
074      preGetCounter.increment();
075    }
076
077    @Override
078    public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
079        List<Cell> results) throws IOException {
080      // do a costly (high latency) operation which we want to measure how long it takes by
081      // using a Timer (which is a Meter and a Histogram).
082      long start = System.nanoTime();
083      try {
084        performCostlyOperation();
085      } finally {
086        costlyOperationTimer.updateNanos(System.nanoTime() - start);
087      }
088    }
089
090    @Override
091    public void postFlush(
092        ObserverContext<RegionCoprocessorEnvironment> c,
093        FlushLifeCycleTracker tracker) throws IOException {
094      flushCounter.increment();
095    }
096
097    @Override
098    public void postFlush(
099        ObserverContext<RegionCoprocessorEnvironment> c, Store store, StoreFile resultFile,
100        FlushLifeCycleTracker tracker) throws IOException {
101      flushCounter.increment();
102    }
103
104    @Override
105    public void postCompactSelection(
106        ObserverContext<RegionCoprocessorEnvironment> c, Store store,
107        List<? extends StoreFile> selected, CompactionLifeCycleTracker tracker,
108        CompactionRequest request) {
109      if (selected != null) {
110        filesCompactedCounter.increment(selected.size());
111      }
112    }
113
114    private void performCostlyOperation() {
115      try {
116        // simulate the operation by sleeping.
117        Thread.sleep(ThreadLocalRandom.current().nextLong(100));
118      } catch (InterruptedException ignore) {
119      }
120    }
121  }
122
123  @Override public Optional<RegionObserver> getRegionObserver() {
124    return Optional.of(observer);
125  }
126
127  @Override
128  public void start(CoprocessorEnvironment env) throws IOException {
129    // start for the RegionServerObserver will be called only once in the lifetime of the
130    // server. We will construct and register all metrics that we will track across method
131    // invocations.
132
133    if (env instanceof RegionCoprocessorEnvironment) {
134      // Obtain the MetricRegistry for the RegionServer. Metrics from this registry will be reported
135      // at the region server level per-regionserver.
136      MetricRegistry registry =
137          ((RegionCoprocessorEnvironment) env).getMetricRegistryForRegionServer();
138      observer = new ExampleRegionObserver();
139
140      if (preGetCounter == null) {
141        // Create a new Counter, or get the already registered counter.
142        // It is much better to only call this once and save the Counter as a class field instead
143        // of creating the counter every time a coprocessor method is invoked. This will negate
144        // any performance bottleneck coming from map lookups tracking metrics in the registry.
145        // Returned counter instance is shared by all coprocessors of the same class in the same
146        // region server.
147        preGetCounter = registry.counter("preGetRequests");
148      }
149
150      if (costlyOperationTimer == null) {
151        // Create a Timer to track execution times for the costly operation.
152        costlyOperationTimer = registry.timer("costlyOperation");
153      }
154
155      if (flushCounter == null) {
156        // Track the number of flushes that have completed
157        flushCounter = registry.counter("flushesCompleted");
158      }
159
160      if (filesCompactedCounter == null) {
161        // Track the number of files that were compacted (many files may be rewritten in a single
162        // compaction).
163        filesCompactedCounter = registry.counter("filesCompacted");
164      }
165    }
166  }
167
168  @Override
169  public void stop(CoprocessorEnvironment e) throws IOException {
170    // we should NOT remove / deregister the metrics in stop(). The whole registry will be
171    // removed when the last region of the table is closed.
172  }
173}