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