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  import java.util.Arrays;
27  
28  import org.apache.hadoop.hbase.util.ByteStringer;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.fs.FSDataOutputStream;
31  import org.apache.hadoop.fs.FileSystem;
32  import org.apache.hadoop.fs.Path;
33  import org.apache.hadoop.hbase.KeyValueUtil;
34  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
35  import org.apache.hadoop.hbase.protobuf.generated.FSProtos;
36  import org.apache.hadoop.hbase.util.Bytes;
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    * @deprecated need by pb serialization
101    */
102   @Deprecated
103   // Make this private when it comes time to let go of this constructor.
104   // Needed by pb serialization.
105   public Reference() {
106     this(null, Range.bottom);
107   }
108 
109   /**
110    *
111    * @return Range
112    */
113   public Range getFileRegion() {
114     return this.region;
115   }
116 
117   /**
118    * @return splitKey
119    */
120   public byte [] getSplitKey() {
121     return splitkey;
122   }
123 
124   /**
125    * @see java.lang.Object#toString()
126    */
127   @Override
128   public String toString() {
129     return "" + this.region;
130   }
131 
132   public static boolean isTopFileRegion(final Range r) {
133     return r.equals(Range.top);
134   }
135 
136   /**
137    * @deprecated Writables are going away. Use the pb serialization methods instead.
138    * Remove in a release after 0.96 goes out.  This is here only to migrate
139    * old Reference files written with Writables before 0.96.
140    */
141   @Deprecated
142   public void readFields(DataInput in) throws IOException {
143     boolean tmp = in.readBoolean();
144     // If true, set region to top.
145     this.region = tmp? Range.top: Range.bottom;
146     this.splitkey = Bytes.readByteArray(in);
147   }
148 
149   public Path write(final FileSystem fs, final Path p)
150   throws IOException {
151     FSDataOutputStream out = fs.create(p, false);
152     try {
153       out.write(toByteArray());
154     } finally {
155       out.close();
156     }
157     return p;
158   }
159 
160   /**
161    * Read a Reference from FileSystem.
162    * @param fs
163    * @param p
164    * @return New Reference made from passed <code>p</code>
165    * @throws IOException
166    */
167   public static Reference read(final FileSystem fs, final Path p)
168   throws IOException {
169     InputStream in = fs.open(p);
170     try {
171       // I need to be able to move back in the stream if this is not a pb serialization so I can
172       // do the Writable decoding instead.
173       in = in.markSupported()? in: new BufferedInputStream(in);
174       int pblen = ProtobufUtil.lengthOfPBMagic();
175       in.mark(pblen);
176       byte [] pbuf = new byte[pblen];
177       int read = in.read(pbuf);
178       if (read != pblen) throw new IOException("read=" + read + ", wanted=" + pblen);
179       // WATCHOUT! Return in middle of function!!!
180       if (ProtobufUtil.isPBMagicPrefix(pbuf)) return convert(FSProtos.Reference.parseFrom(in));
181       // Else presume Writables.  Need to reset the stream since it didn't start w/ pb.
182       // We won't bother rewriting thie Reference as a pb since Reference is transitory.
183       in.reset();
184       Reference r = new Reference();
185       DataInputStream dis = new DataInputStream(in);
186       // Set in = dis so it gets the close below in the finally on our way out.
187       in = dis;
188       r.readFields(dis);
189       return r;
190     } finally {
191       in.close();
192     }
193   }
194 
195   public FSProtos.Reference convert() {
196     FSProtos.Reference.Builder builder = FSProtos.Reference.newBuilder();
197     builder.setRange(isTopFileRegion(getFileRegion())?
198       FSProtos.Reference.Range.TOP: FSProtos.Reference.Range.BOTTOM);
199     builder.setSplitkey(ByteStringer.wrap(getSplitKey()));
200     return builder.build();
201   }
202 
203   public static Reference convert(final FSProtos.Reference r) {
204     Reference result = new Reference();
205     result.splitkey = r.getSplitkey().toByteArray();
206     result.region = r.getRange() == FSProtos.Reference.Range.TOP? Range.top: Range.bottom;
207     return result;
208   }
209 
210   /**
211    * Use this when writing to a stream and you want to use the pb mergeDelimitedFrom
212    * (w/o the delimiter, pb reads to EOF which may not be what you want).
213    * @return This instance serialized as a delimited protobuf w/ a magic pb prefix.
214    * @throws IOException
215    */
216   byte [] toByteArray() throws IOException {
217     return ProtobufUtil.prependPBMagic(convert().toByteArray());
218   }
219 
220   @Override
221   public int hashCode() {
222     return Arrays.hashCode(splitkey) + region.hashCode();
223   }
224 
225   public boolean equals(Object o) {
226     if (this == o) return true;
227     if (o == null) return false;
228     if (!(o instanceof Reference)) return false;
229 
230     Reference r = (Reference) o;
231     if (splitkey != null && r.splitkey == null) return false;
232     if (splitkey == null && r.splitkey != null) return false;
233     if (splitkey != null && !Arrays.equals(splitkey, r.splitkey)) return false;
234 
235     return region.equals(r.region);
236   }
237 }