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.io;
20  
21  import java.io.BufferedInputStream;
22  import java.io.DataInput;
23  import java.io.DataInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  
27  import org.apache.hadoop.classification.InterfaceAudience;
28  import org.apache.hadoop.fs.FSDataOutputStream;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.hbase.KeyValueUtil;
32  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
33  import org.apache.hadoop.hbase.protobuf.generated.FSProtos;
34  import org.apache.hadoop.hbase.util.Bytes;
35  
36  import com.google.protobuf.HBaseZeroCopyByteString;
37  
38  /**
39   * A reference to the top or bottom half of a store file where 'bottom' is the first half
40   * of the file containing the keys that sort lowest and 'top' is the second half
41   * of the file with keys that sort greater than those of the bottom half.  The file referenced
42   * lives under a different region.  References are made at region split time.
43   *
44   * <p>References work with a special half store file type.  References know how
45   * to write out the reference format in the file system and are what is juggled
46   * when references are mixed in with direct store files.  The half store file
47   * type is used reading the referred to file.
48   *
49   * <p>References to store files located over in some other region look like
50   * this in the file system
51   * <code>1278437856009925445.3323223323</code>:
52   * i.e. an id followed by hash of the referenced region.
53   * Note, a region is itself not splittable if it has instances of store file
54   * references.  References are cleaned up by compactions.
55   */
56  @InterfaceAudience.Private
57  public class Reference {
58    private byte [] splitkey;
59    private Range region;
60  
61    /**
62     * For split HStoreFiles, it specifies if the file covers the lower half or
63     * the upper half of the key range
64     */
65    static enum Range {
66      /** HStoreFile contains upper half of key range */
67      top,
68      /** HStoreFile contains lower half of key range */
69      bottom
70    }
71  
72    /**
73     * @param splitRow
74     * @return A {@link Reference} that points at top half of a an hfile
75     */
76    public static Reference createTopReference(final byte [] splitRow) {
77      return new Reference(splitRow, Range.top);
78    }
79  
80    /**
81     * @param splitRow
82     * @return A {@link Reference} that points at the bottom half of a an hfile
83     */
84    public static Reference createBottomReference(final byte [] splitRow) {
85      return new Reference(splitRow, Range.bottom);
86    }
87  
88    /**
89     * Constructor
90     * @param splitRow This is row we are splitting around.
91     * @param fr
92     */
93    Reference(final byte [] splitRow, final Range fr) {
94      this.splitkey = splitRow == null?  null: KeyValueUtil.createFirstOnRow(splitRow).getKey();
95      this.region = fr;
96    }
97  
98    /**
99     * Used by serializations.
100    */
101   @Deprecated
102   // Make this private when it comes time to let go of this constructor.  Needed by pb serialization.
103   public Reference() {
104     this(null, Range.bottom);
105   }
106 
107   /**
108    *
109    * @return Range
110    */
111   public Range getFileRegion() {
112     return this.region;
113   }
114 
115   /**
116    * @return splitKey
117    */
118   public byte [] getSplitKey() {
119     return splitkey;
120   }
121 
122   /**
123    * @see java.lang.Object#toString()
124    */
125   @Override
126   public String toString() {
127     return "" + this.region;
128   }
129 
130   public static boolean isTopFileRegion(final Range r) {
131     return r.equals(Range.top);
132   }
133 
134   /**
135    * @deprecated Writables are going away. Use the pb serialization methods instead.
136    * Remove in a release after 0.96 goes out.  This is here only to migrate
137    * old Reference files written with Writables before 0.96.
138    */
139   @Deprecated
140   public void readFields(DataInput in) throws IOException {
141     boolean tmp = in.readBoolean();
142     // If true, set region to top.
143     this.region = tmp? Range.top: Range.bottom;
144     this.splitkey = Bytes.readByteArray(in);
145   }
146 
147   public Path write(final FileSystem fs, final Path p)
148   throws IOException {
149     FSDataOutputStream out = fs.create(p, false);
150     try {
151       out.write(toByteArray());
152     } finally {
153       out.close();
154     }
155     return p;
156   }
157 
158   /**
159    * Read a Reference from FileSystem.
160    * @param fs
161    * @param p
162    * @return New Reference made from passed <code>p</code>
163    * @throws IOException
164    */
165   public static Reference read(final FileSystem fs, final Path p)
166   throws IOException {
167     InputStream in = fs.open(p);
168     try {
169       // I need to be able to move back in the stream if this is not a pb serialization so I can
170       // do the Writable decoding instead.
171       in = in.markSupported()? in: new BufferedInputStream(in);
172       int pblen = ProtobufUtil.lengthOfPBMagic();
173       in.mark(pblen);
174       byte [] pbuf = new byte[pblen];
175       int read = in.read(pbuf);
176       if (read != pblen) throw new IOException("read=" + read + ", wanted=" + pblen);
177       // WATCHOUT! Return in middle of function!!!
178       if (ProtobufUtil.isPBMagicPrefix(pbuf)) return convert(FSProtos.Reference.parseFrom(in));
179       // Else presume Writables.  Need to reset the stream since it didn't start w/ pb.
180       // We won't bother rewriting thie Reference as a pb since Reference is transitory.
181       in.reset();
182       Reference r = new Reference();
183       DataInputStream dis = new DataInputStream(in);
184       // Set in = dis so it gets the close below in the finally on our way out.
185       in = dis;
186       r.readFields(dis);
187       return r;
188     } finally {
189       in.close();
190     }
191   }
192 
193   public FSProtos.Reference convert() {
194     FSProtos.Reference.Builder builder = FSProtos.Reference.newBuilder();
195     builder.setRange(isTopFileRegion(getFileRegion())?
196       FSProtos.Reference.Range.TOP: FSProtos.Reference.Range.BOTTOM);
197     builder.setSplitkey(HBaseZeroCopyByteString.wrap(getSplitKey()));
198     return builder.build();
199   }
200 
201   public static Reference convert(final FSProtos.Reference r) {
202     Reference result = new Reference();
203     result.splitkey = r.getSplitkey().toByteArray();
204     result.region = r.getRange() == FSProtos.Reference.Range.TOP? Range.top: Range.bottom;
205     return result;
206   }
207 
208   /**
209    * Use this when writing to a stream and you want to use the pb mergeDelimitedFrom
210    * (w/o the delimiter, pb reads to EOF which may not be what you want).
211    * @return This instance serialized as a delimited protobuf w/ a magic pb prefix.
212    * @throws IOException
213    */
214   byte [] toByteArray() throws IOException {
215     return ProtobufUtil.prependPBMagic(convert().toByteArray());
216   }
217 }