/* * Copyright 2010 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** Provides client classes for invoking Coprocessor RPC protocols <p> <ul> <li><a href="#overview">Overview</a></li> <li><a href="#usage">Example Usage</a></li> </ul> </p> <h2><a name="overview">Overview</a></h2> <p> The coprocessor framework provides a way for custom code to run in place on the HBase region servers with each of a table's regions. These client classes enable applications to communicate with coprocessor instances via custom RPC protocols. </p> <p> In order to provide a custom RPC protocol to clients, a coprocessor implementation defines an interface that extends {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol}. The interface can define any methods that the coprocessor wishes to expose. Using this protocol, you can communicate with the coprocessor instances via the {@link org.apache.hadoop.hbase.client.HTable#coprocessorProxy(Class, byte[])} and {@link org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)} methods. </p> <p> Since {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol} instances are associated with individual regions within the table, the client RPC calls must ultimately identify which regions should be used in the <code>CoprocessorProtocol</code> method invocations. Since regions are seldom handled directly in client code and the region names may change over time, the coprocessor RPC calls use row keys to identify which regions should be used for the method invocations. Clients can call <code>CoprocessorProtocol</code> methods against either: <ul> <li><strong>a single region</strong> - calling {@link org.apache.hadoop.hbase.client.HTable#coprocessorProxy(Class, byte[])} with a single row key. This returns a dynamic proxy of the <code>CoprocessorProtocol</code> interface which uses the region containing the given row key (even if the row does not exist) as the RPC endpoint.</li> <li><strong>a range of regions</strong> - calling {@link org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call, org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)} with a starting row key and an ending row key. All regions in the table from the region containing the start row key to the region containing the end row key (inclusive), will we used as the RPC endpoints.</li> </ul> </p> <p><em>Note that the row keys passed as parameters to the <code>HTable</code> methods are not passed to the <code>CoprocessorProtocol</code> implementations. They are only used to identify the regions for endpoints of the remote calls. </em></p> <p> The {@link org.apache.hadoop.hbase.client.coprocessor.Batch} class defines two interfaces used for <code>CoprocessorProtocol</code> invocations against multiple regions. Clients implement {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call} to call methods of the actual <code>CoprocessorProtocol</code> instance. The interface's <code>call()</code> method will be called once per selected region, passing the <code>CoprocessorProtocol</code> instance for the region as a parameter. Clients can optionally implement {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Callback} to be notified of the results from each region invocation as they complete. The instance's {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Callback#update(byte[], byte[], Object)} method will be called with the {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call#call(Object)} return value from each region. </p> <h2><a name="usage">Example usage</a></h2> <p> To start with, let's use a fictitious coprocessor, <code>RowCountCoprocessor</code> that counts the number of rows and key-values in each region where it is running. For clients to query this information, the coprocessor defines and implements the following {@link org.apache.hadoop.hbase.ipc.CoprocessorProtocol} extension interface: </p> <div style="background-color: #cccccc; padding: 2px"> <blockquote><pre> public interface RowCountProtocol extends CoprocessorProtocol { long getRowCount(); long getRowCount(Filter filt); long getKeyValueCount(); } </pre></blockquote></div> <p> Now we need a way to access the results that <code>RowCountCoprocessor</code> is making available. If we want to find the row count for all regions, we could use: </p> <div style="background-color: #cccccc; padding: 2px"> <blockquote><pre> HTable table = new HTable("mytable"); // find row count keyed by region name Map<byte[],Long> results = table.coprocessorExec( RowCountProtocol.class, // the protocol interface we're invoking null, null, // start and end row keys new Batch.Call<RowCountProtocol,Long>() { public Long call(RowCountProtocol counter) { return counter.getRowCount(); } }); </pre></blockquote></div> <p> This will return a <code>java.util.Map</code> of the <code>counter.getRowCount()</code> result for the <code>RowCountCoprocessor</code> instance running in each region of <code>mytable</code>, keyed by the region name. </p> <p> By implementing {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call} as an anonymous class, we can invoke <code>RowCountProtocol</code> methods directly against the {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call#call(Object)} method's argument. Calling {@link org.apache.hadoop.hbase.client.HTable#coprocessorExec(Class, byte[], byte[], org.apache.hadoop.hbase.client.coprocessor.Batch.Call)} will take care of invoking <code>Batch.Call.call()</code> against our anonymous class with the <code>RowCountCoprocessor</code> instance for each table region. </p> <p> For this simple case, where we only want to obtain the result from a single <code>CoprocessorProtocol</code> method, there's also a bit of syntactic sugar we can use to cut down on the amount of code required: </p> <div style="background-color: #cccccc; padding: 2px"> <blockquote><pre> HTable table = new HTable("mytable"); Batch.Call<RowCountProtocol,Long> call = Batch.forMethod(RowCountProtocol.class, "getRowCount"); Map<byte[],Long> results = table.coprocessorExec(RowCountProtocol.class, null, null, call); </pre></blockquote></div> <p> {@link org.apache.hadoop.hbase.client.coprocessor.Batch#forMethod(Class, String, Object...)} is a simple factory method that will return a {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call} instance that will call <code>RowCountProtocol.getRowCount()</code> for us using reflection. </p> <p> However, if you want to perform additional processing on the results, implementing {@link org.apache.hadoop.hbase.client.coprocessor.Batch.Call} directly will provide more power and flexibility. For example, if you would like to combine row count and key-value count for each region: </p> <div style="background-color: #cccccc; padding: 2px"> <blockquote><pre> HTable table = new HTable("mytable"); // combine row count and kv count for region Map<byte[],Pair<Long,Long>> results = table.coprocessorExec( RowCountProtocol.class, null, null, new Batch.Call<RowCountProtocol,Pair<Long,Long>>() { public Pair<Long,Long> call(RowCountProtocol counter) { return new Pair(counter.getRowCount(), counter.getKeyValueCount()); } }); </pre></blockquote></div> <p> Similarly, you could average the number of key-values per row for each region: </p> <div style="background-color: #cccccc; padding: 2px"> <blockquote><pre> Map<byte[],Double> results = table.coprocessorExec( RowCountProtocol.class, null, null, new Batch.Call<RowCountProtocol,Double>() { public Double call(RowCountProtocol counter) { return ((double)counter.getKeyValueCount()) / ((double)counter.getRowCount()); } }); </pre></blockquote></div> */ package org.apache.hadoop.hbase.client.coprocessor;