001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.io; 020 021import java.io.BufferedInputStream; 022import java.io.DataInput; 023import java.io.DataInputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.util.Arrays; 027 028import org.apache.yetus.audience.InterfaceAudience; 029import org.apache.hadoop.fs.FSDataOutputStream; 030import org.apache.hadoop.fs.FileSystem; 031import org.apache.hadoop.fs.Path; 032import org.apache.hadoop.hbase.KeyValueUtil; 033import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 034import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 035import org.apache.hadoop.hbase.shaded.protobuf.generated.FSProtos; 036import org.apache.hadoop.hbase.util.Bytes; 037 038/** 039 * A reference to the top or bottom half of a store file where 'bottom' is the first half 040 * of the file containing the keys that sort lowest and 'top' is the second half 041 * of the file with keys that sort greater than those of the bottom half. The file referenced 042 * lives under a different region. References are made at region split time. 043 * 044 * <p>References work with a special half store file type. References know how 045 * to write out the reference format in the file system and are what is juggled 046 * when references are mixed in with direct store files. The half store file 047 * type is used reading the referred to file. 048 * 049 * <p>References to store files located over in some other region look like 050 * this in the file system 051 * <code>1278437856009925445.3323223323</code>: 052 * i.e. an id followed by hash of the referenced region. 053 * Note, a region is itself not splittable if it has instances of store file 054 * references. References are cleaned up by compactions. 055 */ 056@InterfaceAudience.Private 057public class Reference { 058 private byte [] splitkey; 059 private Range region; 060 061 /** 062 * For split HStoreFiles, it specifies if the file covers the lower half or 063 * the upper half of the key range 064 */ 065 static enum Range { 066 /** HStoreFile contains upper half of key range */ 067 top, 068 /** HStoreFile contains lower half of key range */ 069 bottom 070 } 071 072 /** 073 * @param splitRow 074 * @return A {@link Reference} that points at top half of a an hfile 075 */ 076 public static Reference createTopReference(final byte [] splitRow) { 077 return new Reference(splitRow, Range.top); 078 } 079 080 /** 081 * @param splitRow 082 * @return A {@link Reference} that points at the bottom half of a an hfile 083 */ 084 public static Reference createBottomReference(final byte [] splitRow) { 085 return new Reference(splitRow, Range.bottom); 086 } 087 088 /** 089 * Constructor 090 * @param splitRow This is row we are splitting around. 091 * @param fr 092 */ 093 Reference(final byte [] splitRow, final Range fr) { 094 this.splitkey = splitRow == null? null: KeyValueUtil.createFirstOnRow(splitRow).getKey(); 095 this.region = fr; 096 } 097 098 /** 099 * 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) { 179 throw new IOException("read=" + read + ", wanted=" + pblen); 180 } 181 // WATCHOUT! Return in middle of function!!! 182 if (ProtobufUtil.isPBMagicPrefix(pbuf)) return convert(FSProtos.Reference.parseFrom(in)); 183 // Else presume Writables. Need to reset the stream since it didn't start w/ pb. 184 // We won't bother rewriting thie Reference as a pb since Reference is transitory. 185 in.reset(); 186 Reference r = new Reference(); 187 DataInputStream dis = new DataInputStream(in); 188 // Set in = dis so it gets the close below in the finally on our way out. 189 in = dis; 190 r.readFields(dis); 191 return r; 192 } finally { 193 in.close(); 194 } 195 } 196 197 public FSProtos.Reference convert() { 198 FSProtos.Reference.Builder builder = FSProtos.Reference.newBuilder(); 199 builder.setRange(isTopFileRegion(getFileRegion())? 200 FSProtos.Reference.Range.TOP: FSProtos.Reference.Range.BOTTOM); 201 builder.setSplitkey(UnsafeByteOperations.unsafeWrap(getSplitKey())); 202 return builder.build(); 203 } 204 205 public static Reference convert(final FSProtos.Reference r) { 206 Reference result = new Reference(); 207 result.splitkey = r.getSplitkey().toByteArray(); 208 result.region = r.getRange() == FSProtos.Reference.Range.TOP? Range.top: Range.bottom; 209 return result; 210 } 211 212 /** 213 * Use this when writing to a stream and you want to use the pb mergeDelimitedFrom 214 * (w/o the delimiter, pb reads to EOF which may not be what you want). 215 * @return This instance serialized as a delimited protobuf w/ a magic pb prefix. 216 * @throws IOException 217 */ 218 byte [] toByteArray() throws IOException { 219 return ProtobufUtil.prependPBMagic(convert().toByteArray()); 220 } 221 222 @Override 223 public int hashCode() { 224 return Arrays.hashCode(splitkey) + region.hashCode(); 225 } 226 227 @Override 228 public boolean equals(Object o) { 229 if (this == o) return true; 230 if (o == null) return false; 231 if (!(o instanceof Reference)) return false; 232 233 Reference r = (Reference) o; 234 if (splitkey != null && r.splitkey == null) return false; 235 if (splitkey == null && r.splitkey != null) return false; 236 if (splitkey != null && !Arrays.equals(splitkey, r.splitkey)) return false; 237 238 return region.equals(r.region); 239 } 240}