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