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.mapreduce;
20  
21  import java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.util.Arrays;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.client.Scan;
33  import org.apache.hadoop.hbase.util.Bytes;
34  import org.apache.hadoop.io.Writable;
35  import org.apache.hadoop.io.WritableUtils;
36  import org.apache.hadoop.mapreduce.InputSplit;
37  
38  /**
39   * A table split corresponds to a key range (low, high) and an optional scanner.
40   * All references to row below refer to the key of the row.
41   */
42  @InterfaceAudience.Public
43  @InterfaceStability.Evolving
44  public class TableSplit extends InputSplit
45  implements Writable, Comparable<TableSplit> {
46    public static final Log LOG = LogFactory.getLog(TableSplit.class);
47    
48    // should be < 0 (@see #readFields(DataInput))
49    // version 1 supports Scan data member
50    enum Version {
51      UNVERSIONED(0),
52      // Initial number we put on TableSplit when we introduced versioning.
53      INITIAL(-1);
54  
55      final int code;
56      static final Version[] byCode;
57      static {
58        byCode = Version.values();
59        for (int i = 0; i < byCode.length; i++) {
60          if (byCode[i].code != -1 * i) {
61            throw new AssertionError("Values in this enum should be descending by one");
62          }
63        }
64      }
65  
66      Version(int code) {
67        this.code = code;
68      }
69  
70      boolean atLeast(Version other) {
71        return code <= other.code;
72      }
73  
74      static Version fromCode(int code) {
75        return byCode[code * -1];
76      }
77    }
78    
79    private static final Version VERSION = Version.INITIAL;
80    private TableName tableName;
81    private byte [] startRow;
82    private byte [] endRow;
83    private String regionLocation;
84    private String scan = ""; // stores the serialized form of the Scan
85    private long length; // Contains estimation of region size in bytes
86  
87    /** Default constructor. */
88    public TableSplit() {
89      this((TableName)null, null, HConstants.EMPTY_BYTE_ARRAY,
90        HConstants.EMPTY_BYTE_ARRAY, "");
91    }
92  
93    /**
94     * @deprecated As of release 0.96
95     *             (<a href="https://issues.apache.org/jira/browse/HBASE-9508">HBASE-9508</a>).
96     *             This will be removed in HBase 2.0.0.
97     *             Use {@link TableSplit#TableSplit(TableName, byte[], byte[], String)}.
98     */
99    @Deprecated
100   public TableSplit(final byte [] tableName, Scan scan, byte [] startRow, byte [] endRow,
101       final String location) {
102     this(TableName.valueOf(tableName), scan, startRow, endRow, location);
103   }
104 
105   /**
106    * Creates a new instance while assigning all variables.
107    * Length of region is set to 0
108    *
109    * @param tableName  The name of the current table.
110    * @param scan The scan associated with this split.
111    * @param startRow  The start row of the split.
112    * @param endRow  The end row of the split.
113    * @param location  The location of the region.
114    */
115   public TableSplit(TableName tableName, Scan scan, byte [] startRow, byte [] endRow,
116                     final String location) {
117     this(tableName, scan, startRow, endRow, location, 0L);
118   }
119 
120   /**
121    * Creates a new instance while assigning all variables.
122    *
123    * @param tableName  The name of the current table.
124    * @param scan The scan associated with this split.
125    * @param startRow  The start row of the split.
126    * @param endRow  The end row of the split.
127    * @param location  The location of the region.
128    */
129   public TableSplit(TableName tableName, Scan scan, byte [] startRow, byte [] endRow,
130       final String location, long length) {
131     this.tableName = tableName;
132     try {
133       this.scan =
134         (null == scan) ? "" : TableMapReduceUtil.convertScanToString(scan);
135     } catch (IOException e) {
136       LOG.warn("Failed to convert Scan to String", e);
137     }
138     this.startRow = startRow;
139     this.endRow = endRow;
140     this.regionLocation = location;
141     this.length = length;
142   }
143 
144   /**
145    * @deprecated As of release 0.96
146    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9508">HBASE-9508</a>).
147    *             This will be removed in HBase 2.0.0.
148    *             Use {@link TableSplit#TableSplit(TableName, byte[], byte[], String)}.
149    */
150   @Deprecated
151   public TableSplit(final byte [] tableName, byte[] startRow, byte[] endRow,
152       final String location) {
153     this(TableName.valueOf(tableName), startRow, endRow, location);
154   }
155 
156   /**
157    * Creates a new instance without a scanner.
158    *
159    * @param tableName The name of the current table.
160    * @param startRow The start row of the split.
161    * @param endRow The end row of the split.
162    * @param location The location of the region.
163    */
164   public TableSplit(TableName tableName, byte[] startRow, byte[] endRow,
165       final String location) {
166     this(tableName, null, startRow, endRow, location);
167   }
168 
169   /**
170    * Creates a new instance without a scanner.
171    *
172    * @param tableName The name of the current table.
173    * @param startRow The start row of the split.
174    * @param endRow The end row of the split.
175    * @param location The location of the region.
176    * @param length Size of region in bytes
177    */
178   public TableSplit(TableName tableName, byte[] startRow, byte[] endRow,
179                     final String location, long length) {
180     this(tableName, null, startRow, endRow, location, length);
181   }
182 
183   /**
184    * Returns a Scan object from the stored string representation.
185    *
186    * @return Returns a Scan object based on the stored scanner.
187    * @throws IOException
188    */
189   public Scan getScan() throws IOException {
190     return TableMapReduceUtil.convertStringToScan(this.scan);
191   }
192 
193   /**
194    * Returns the table name converted to a byte array.
195    * @see #getTable()
196    * @return The table name.
197    */
198   public byte [] getTableName() {
199     return tableName.getName();
200   }
201 
202   /**
203    * Returns the table name.
204    *
205    * @return The table name.
206    */
207   public TableName getTable() {
208     // It is ugly that usually to get a TableName, the method is called getTableName.  We can't do
209     // that in here though because there was an existing getTableName in place already since
210     // deprecated.
211     return tableName;
212   }
213 
214   /**
215    * Returns the start row.
216    *
217    * @return The start row.
218    */
219   public byte [] getStartRow() {
220     return startRow;
221   }
222 
223   /**
224    * Returns the end row.
225    *
226    * @return The end row.
227    */
228   public byte [] getEndRow() {
229     return endRow;
230   }
231 
232   /**
233    * Returns the region location.
234    *
235    * @return The region's location.
236    */
237   public String getRegionLocation() {
238     return regionLocation;
239   }
240 
241   /**
242    * Returns the region's location as an array.
243    *
244    * @return The array containing the region location.
245    * @see org.apache.hadoop.mapreduce.InputSplit#getLocations()
246    */
247   @Override
248   public String[] getLocations() {
249     return new String[] {regionLocation};
250   }
251 
252   /**
253    * Returns the length of the split.
254    *
255    * @return The length of the split.
256    * @see org.apache.hadoop.mapreduce.InputSplit#getLength()
257    */
258   @Override
259   public long getLength() {
260     return length;
261   }
262 
263   /**
264    * Reads the values of each field.
265    *
266    * @param in  The input to read from.
267    * @throws IOException When reading the input fails.
268    */
269   @Override
270   public void readFields(DataInput in) throws IOException {
271     Version version = Version.UNVERSIONED;
272     // TableSplit was not versioned in the beginning.
273     // In order to introduce it now, we make use of the fact
274     // that tableName was written with Bytes.writeByteArray,
275     // which encodes the array length as a vint which is >= 0.
276     // Hence if the vint is >= 0 we have an old version and the vint
277     // encodes the length of tableName.
278     // If < 0 we just read the version and the next vint is the length.
279     // @see Bytes#readByteArray(DataInput)
280     int len = WritableUtils.readVInt(in);
281     if (len < 0) {
282       // what we just read was the version
283       version = Version.fromCode(len);
284       len = WritableUtils.readVInt(in);
285     }
286     byte[] tableNameBytes = new byte[len];
287     in.readFully(tableNameBytes);
288     tableName = TableName.valueOf(tableNameBytes);
289     startRow = Bytes.readByteArray(in);
290     endRow = Bytes.readByteArray(in);
291     regionLocation = Bytes.toString(Bytes.readByteArray(in));
292     if (version.atLeast(Version.INITIAL)) {
293       scan = Bytes.toString(Bytes.readByteArray(in));
294     }
295     length = WritableUtils.readVLong(in);
296   }
297 
298   /**
299    * Writes the field values to the output.
300    *
301    * @param out  The output to write to.
302    * @throws IOException When writing the values to the output fails.
303    */
304   @Override
305   public void write(DataOutput out) throws IOException {
306     WritableUtils.writeVInt(out, VERSION.code);
307     Bytes.writeByteArray(out, tableName.getName());
308     Bytes.writeByteArray(out, startRow);
309     Bytes.writeByteArray(out, endRow);
310     Bytes.writeByteArray(out, Bytes.toBytes(regionLocation));
311     Bytes.writeByteArray(out, Bytes.toBytes(scan));
312     WritableUtils.writeVLong(out, length);
313   }
314 
315   /**
316    * Returns the details about this instance as a string.
317    *
318    * @return The values of this instance as a string.
319    * @see java.lang.Object#toString()
320    */
321   @Override
322   public String toString() {
323     StringBuilder sb = new StringBuilder();
324     sb.append("HBase table split(");
325     sb.append("table name: ").append(tableName);
326     sb.append(", scan: ").append(scan);
327     sb.append(", start row: ").append(Bytes.toStringBinary(startRow));
328     sb.append(", end row: ").append(Bytes.toStringBinary(endRow));
329     sb.append(", region location: ").append(regionLocation);
330     sb.append(")");
331     return sb.toString();
332   }
333 
334   /**
335    * Compares this split against the given one.
336    *
337    * @param split  The split to compare to.
338    * @return The result of the comparison.
339    * @see java.lang.Comparable#compareTo(java.lang.Object)
340    */
341   @Override
342   public int compareTo(TableSplit split) {
343     // If The table name of the two splits is the same then compare start row
344     // otherwise compare based on table names
345     int tableNameComparison =
346         getTable().compareTo(split.getTable());
347     return tableNameComparison != 0 ? tableNameComparison : Bytes.compareTo(
348         getStartRow(), split.getStartRow());
349   }
350 
351   @Override
352   public boolean equals(Object o) {
353     if (o == null || !(o instanceof TableSplit)) {
354       return false;
355     }
356     return tableName.equals(((TableSplit)o).tableName) &&
357       Bytes.equals(startRow, ((TableSplit)o).startRow) &&
358       Bytes.equals(endRow, ((TableSplit)o).endRow) &&
359       regionLocation.equals(((TableSplit)o).regionLocation);
360   }
361 
362     @Override
363     public int hashCode() {
364         int result = tableName != null ? tableName.hashCode() : 0;
365         result = 31 * result + (scan != null ? scan.hashCode() : 0);
366         result = 31 * result + (startRow != null ? Arrays.hashCode(startRow) : 0);
367         result = 31 * result + (endRow != null ? Arrays.hashCode(endRow) : 0);
368         result = 31 * result + (regionLocation != null ? regionLocation.hashCode() : 0);
369         return result;
370     }
371 }