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.regionserver; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.DataInputStream; 024import java.io.DataOutputStream; 025import java.io.IOException; 026import java.util.concurrent.atomic.AtomicLong; 027 028import org.apache.hadoop.hbase.Cell; 029import org.apache.hadoop.hbase.CellUtil; 030import org.apache.hadoop.hbase.PrivateCellUtil; 031import org.apache.hadoop.hbase.io.TimeRange; 032import org.apache.yetus.audience.InterfaceAudience; 033 034import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 035import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 036import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 037import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 038 039/** 040 * Stores minimum and maximum timestamp values, it is [minimumTimestamp, maximumTimestamp] in 041 * interval notation. 042 * Use this class at write-time ONLY. Too much synchronization to use at read time 043 * Use {@link TimeRange} at read time instead of this. See toTimeRange() to make TimeRange to use. 044 * MemStores use this class to track minimum and maximum timestamps. The TimeRangeTracker made by 045 * the MemStore is passed to the StoreFile for it to write out as part a flush in the the file 046 * metadata. If no memstore involved -- i.e. a compaction -- then the StoreFile will calculate its 047 * own TimeRangeTracker as it appends. The StoreFile serialized TimeRangeTracker is used 048 * at read time via an instance of {@link TimeRange} to test if Cells fit the StoreFile TimeRange. 049 */ 050@InterfaceAudience.Private 051public abstract class TimeRangeTracker { 052 053 public enum Type { 054 // thread-unsafe 055 NON_SYNC, 056 // thread-safe 057 SYNC 058 } 059 060 static final long INITIAL_MIN_TIMESTAMP = Long.MAX_VALUE; 061 static final long INITIAL_MAX_TIMESTAMP = -1L; 062 063 public static TimeRangeTracker create(Type type) { 064 switch (type) { 065 case NON_SYNC: 066 return new NonSyncTimeRangeTracker(); 067 case SYNC: 068 return new SyncTimeRangeTracker(); 069 default: 070 throw new UnsupportedOperationException("The type:" + type + " is unsupported"); 071 } 072 } 073 074 public static TimeRangeTracker create(Type type, TimeRangeTracker trt) { 075 switch (type) { 076 case NON_SYNC: 077 return new NonSyncTimeRangeTracker(trt); 078 case SYNC: 079 return new SyncTimeRangeTracker(trt); 080 default: 081 throw new UnsupportedOperationException("The type:" + type + " is unsupported"); 082 } 083 } 084 085 public static TimeRangeTracker create(Type type, long minimumTimestamp, long maximumTimestamp) { 086 switch (type) { 087 case NON_SYNC: 088 return new NonSyncTimeRangeTracker(minimumTimestamp, maximumTimestamp); 089 case SYNC: 090 return new SyncTimeRangeTracker(minimumTimestamp, maximumTimestamp); 091 default: 092 throw new UnsupportedOperationException("The type:" + type + " is unsupported"); 093 } 094 } 095 096 protected abstract void setMax(long ts); 097 protected abstract void setMin(long ts); 098 protected abstract boolean compareAndSetMin(long expect, long update); 099 protected abstract boolean compareAndSetMax(long expect, long update); 100 /** 101 * Update the current TimestampRange to include the timestamp from <code>cell</code>. 102 * If the Key is of type DeleteColumn or DeleteFamily, it includes the 103 * entire time range from 0 to timestamp of the key. 104 * @param cell the Cell to include 105 */ 106 public void includeTimestamp(final Cell cell) { 107 includeTimestamp(cell.getTimestamp()); 108 if (PrivateCellUtil.isDeleteColumnOrFamily(cell)) { 109 includeTimestamp(0); 110 } 111 } 112 113 /** 114 * If required, update the current TimestampRange to include timestamp 115 * @param timestamp the timestamp value to include 116 */ 117 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="MT_CORRECTNESS", 118 justification="Intentional") 119 void includeTimestamp(final long timestamp) { 120 long initialMinTimestamp = getMin(); 121 if (timestamp < initialMinTimestamp) { 122 long curMinTimestamp = initialMinTimestamp; 123 while (timestamp < curMinTimestamp) { 124 if (!compareAndSetMin(curMinTimestamp, timestamp)) { 125 curMinTimestamp = getMin(); 126 } else { 127 // successfully set minimumTimestamp, break. 128 break; 129 } 130 } 131 132 // When it reaches here, there are two possibilities: 133 // 1). timestamp >= curMinTimestamp, someone already sets the minimumTimestamp. In this case, 134 // it still needs to check if initialMinTimestamp == INITIAL_MIN_TIMESTAMP to see 135 // if it needs to update minimumTimestamp. Someone may already set both 136 // minimumTimestamp/minimumTimestamp to the same value(curMinTimestamp), 137 // need to check if maximumTimestamp needs to be updated. 138 // 2). timestamp < curMinTimestamp, it sets the minimumTimestamp successfully. 139 // In this case,it still needs to check if initialMinTimestamp == INITIAL_MIN_TIMESTAMP 140 // to see if it needs to set maximumTimestamp. 141 if (initialMinTimestamp != INITIAL_MIN_TIMESTAMP) { 142 // Someone already sets minimumTimestamp and timestamp is less than minimumTimestamp. 143 // In this case, no need to set maximumTimestamp as it will be set to at least 144 // initialMinTimestamp. 145 return; 146 } 147 } 148 149 long curMaxTimestamp = getMax(); 150 151 if (timestamp > curMaxTimestamp) { 152 while (timestamp > curMaxTimestamp) { 153 if (!compareAndSetMax(curMaxTimestamp, timestamp)) { 154 curMaxTimestamp = getMax(); 155 } else { 156 // successfully set maximumTimestamp, break 157 break; 158 } 159 } 160 } 161 } 162 163 /** 164 * Check if the range has ANY overlap with TimeRange 165 * @param tr TimeRange, it expects [minStamp, maxStamp) 166 * @return True if there is overlap, false otherwise 167 */ 168 public boolean includesTimeRange(final TimeRange tr) { 169 return (getMin() < tr.getMax() && getMax() >= tr.getMin()); 170 } 171 172 /** 173 * @return the minimumTimestamp 174 */ 175 public abstract long getMin(); 176 177 /** 178 * @return the maximumTimestamp 179 */ 180 public abstract long getMax(); 181 182 @Override 183 public String toString() { 184 return "[" + getMin() + "," + getMax() + "]"; 185 } 186 187 /** 188 * @param data the serialization data. It can't be null! 189 * @return An instance of NonSyncTimeRangeTracker filled w/ the content of serialized 190 * NonSyncTimeRangeTracker in <code>timeRangeTrackerBytes</code>. 191 * @throws IOException 192 */ 193 public static TimeRangeTracker parseFrom(final byte[] data) throws IOException { 194 return parseFrom(data, Type.NON_SYNC); 195 } 196 197 public static TimeRangeTracker parseFrom(final byte[] data, Type type) throws IOException { 198 Preconditions.checkNotNull(data, "input data is null!"); 199 if (ProtobufUtil.isPBMagicPrefix(data)) { 200 int pblen = ProtobufUtil.lengthOfPBMagic(); 201 HBaseProtos.TimeRangeTracker.Builder builder = HBaseProtos.TimeRangeTracker.newBuilder(); 202 ProtobufUtil.mergeFrom(builder, data, pblen, data.length - pblen); 203 return TimeRangeTracker.create(type, builder.getFrom(), builder.getTo()); 204 } else { 205 try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(data))) { 206 return TimeRangeTracker.create(type, in.readLong(), in.readLong()); 207 } 208 } 209 } 210 211 /** 212 * This method used to serialize TimeRangeTracker (TRT) by protobuf while this breaks the 213 * forward compatibility on HFile.(See HBASE-21008) In previous hbase version ( < 2.0.0 ) we use 214 * DataOutput to serialize TRT, these old versions don't have capability to deserialize TRT 215 * which is serialized by protobuf. So we need to revert the change of serializing 216 * TimeRangeTracker back to DataOutput. For more information, please check HBASE-21012. 217 * @param tracker TimeRangeTracker needed to be serialized. 218 * @return byte array filled with serialized TimeRangeTracker. 219 * @throws IOException if something goes wrong in writeLong. 220 */ 221 public static byte[] toByteArray(TimeRangeTracker tracker) throws IOException { 222 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 223 try (DataOutputStream dos = new DataOutputStream(bos)) { 224 dos.writeLong(tracker.getMin()); 225 dos.writeLong(tracker.getMax()); 226 return bos.toByteArray(); 227 } 228 } 229 } 230 231 /** 232 * @return Make a TimeRange from current state of <code>this</code>. 233 */ 234 TimeRange toTimeRange() { 235 long min = getMin(); 236 long max = getMax(); 237 // Initial TimeRangeTracker timestamps are the opposite of what you want for a TimeRange. Fix! 238 if (min == INITIAL_MIN_TIMESTAMP) { 239 min = TimeRange.INITIAL_MIN_TIMESTAMP; 240 } 241 if (max == INITIAL_MAX_TIMESTAMP) { 242 max = TimeRange.INITIAL_MAX_TIMESTAMP; 243 } 244 return new TimeRange(min, max); 245 } 246 247 @VisibleForTesting 248 //In order to estimate the heap size, this inner class need to be accessible to TestHeapSize. 249 public static class NonSyncTimeRangeTracker extends TimeRangeTracker { 250 private long minimumTimestamp = INITIAL_MIN_TIMESTAMP; 251 private long maximumTimestamp = INITIAL_MAX_TIMESTAMP; 252 253 NonSyncTimeRangeTracker() { 254 } 255 256 NonSyncTimeRangeTracker(final TimeRangeTracker trt) { 257 this.minimumTimestamp = trt.getMin(); 258 this.maximumTimestamp = trt.getMax(); 259 } 260 261 NonSyncTimeRangeTracker(long minimumTimestamp, long maximumTimestamp) { 262 this.minimumTimestamp = minimumTimestamp; 263 this.maximumTimestamp = maximumTimestamp; 264 } 265 266 @Override 267 protected void setMax(long ts) { 268 maximumTimestamp = ts; 269 } 270 271 @Override 272 protected void setMin(long ts) { 273 minimumTimestamp = ts; 274 } 275 276 @Override 277 protected boolean compareAndSetMin(long expect, long update) { 278 if (minimumTimestamp != expect) { 279 return false; 280 } 281 minimumTimestamp = update; 282 return true; 283 } 284 285 @Override 286 protected boolean compareAndSetMax(long expect, long update) { 287 if (maximumTimestamp != expect) { 288 return false; 289 } 290 maximumTimestamp = update; 291 return true; 292 } 293 294 @Override 295 public long getMin() { 296 return minimumTimestamp; 297 } 298 299 @Override 300 public long getMax() { 301 return maximumTimestamp; 302 } 303 } 304 305 @VisibleForTesting 306 //In order to estimate the heap size, this inner class need to be accessible to TestHeapSize. 307 public static class SyncTimeRangeTracker extends TimeRangeTracker { 308 private final AtomicLong minimumTimestamp = new AtomicLong(INITIAL_MIN_TIMESTAMP); 309 private final AtomicLong maximumTimestamp = new AtomicLong(INITIAL_MAX_TIMESTAMP); 310 311 private SyncTimeRangeTracker() { 312 } 313 314 SyncTimeRangeTracker(final TimeRangeTracker trt) { 315 this.minimumTimestamp.set(trt.getMin()); 316 this.maximumTimestamp.set(trt.getMax()); 317 } 318 319 SyncTimeRangeTracker(long minimumTimestamp, long maximumTimestamp) { 320 this.minimumTimestamp.set(minimumTimestamp); 321 this.maximumTimestamp.set(maximumTimestamp); 322 } 323 324 @Override 325 protected void setMax(long ts) { 326 maximumTimestamp.set(ts); 327 } 328 329 @Override 330 protected void setMin(long ts) { 331 minimumTimestamp.set(ts); 332 } 333 334 @Override 335 protected boolean compareAndSetMin(long expect, long update) { 336 return minimumTimestamp.compareAndSet(expect, update); 337 } 338 339 @Override 340 protected boolean compareAndSetMax(long expect, long update) { 341 return maximumTimestamp.compareAndSet(expect, update); 342 } 343 344 @Override 345 public long getMin() { 346 return minimumTimestamp.get(); 347 } 348 349 @Override 350 public long getMax() { 351 return maximumTimestamp.get(); 352 } 353 } 354}