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.regionserver;
20  
21  import java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.util.concurrent.atomic.AtomicLong;
25  
26  import org.apache.hadoop.hbase.Cell;
27  import org.apache.hadoop.hbase.CellUtil;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.io.TimeRange;
30  import org.apache.hadoop.hbase.util.Writables;
31  import org.apache.hadoop.io.Writable;
32  /**
33   * Stores minimum and maximum timestamp values, it is [minimumTimestamp, maximumTimestamp] in
34   * interval notation.
35   * Use this class at write-time ONLY. Too much synchronization to use at read time
36   * (TODO: there are two scenarios writing, once when lots of concurrency as part of memstore
37   * updates but then later we can make one as part of a compaction when there is only one thread
38   * involved -- consider making different version, the synchronized and the unsynchronized).
39   * Use {@link TimeRange} at read time instead of this. See toTimeRange() to make TimeRange to use.
40   * MemStores use this class to track minimum and maximum timestamps. The TimeRangeTracker made by
41   * the MemStore is passed to the StoreFile for it to write out as part a flush in the the file
42   * metadata. If no memstore involved -- i.e. a compaction -- then the StoreFile will calculate its
43   * own TimeRangeTracker as it appends. The StoreFile serialized TimeRangeTracker is used
44   * at read time via an instance of {@link TimeRange} to test if Cells fit the StoreFile TimeRange.
45   */
46  @InterfaceAudience.Private
47  public class TimeRangeTracker implements Writable {
48    static final long INITIAL_MIN_TIMESTAMP = Long.MAX_VALUE;
49    static final long INITIAL_MAX_TIMESTAMP = -1L;
50  
51    AtomicLong minimumTimestamp = new AtomicLong(INITIAL_MIN_TIMESTAMP);
52    AtomicLong maximumTimestamp = new AtomicLong(INITIAL_MAX_TIMESTAMP);
53  
54    /**
55     * Default constructor.
56     * Initializes TimeRange to be null
57     */
58    public TimeRangeTracker() {}
59  
60    /**
61     * Copy Constructor
62     * @param trt source TimeRangeTracker
63     */
64    public TimeRangeTracker(final TimeRangeTracker trt) {
65      minimumTimestamp.set(trt.getMin());
66      maximumTimestamp.set(trt.getMax());
67    }
68  
69    public TimeRangeTracker(long minimumTimestamp, long maximumTimestamp) {
70      this.minimumTimestamp.set(minimumTimestamp);
71      this.maximumTimestamp.set(maximumTimestamp);
72    }
73  
74    /**
75     * Update the current TimestampRange to include the timestamp from <code>cell</code>.
76     * If the Key is of type DeleteColumn or DeleteFamily, it includes the
77     * entire time range from 0 to timestamp of the key.
78     * @param cell the Cell to include
79     */
80    public void includeTimestamp(final Cell cell) {
81      includeTimestamp(cell.getTimestamp());
82      if (CellUtil.isDeleteColumnOrFamily(cell)) {
83        includeTimestamp(0);
84      }
85    }
86  
87    /**
88     * If required, update the current TimestampRange to include timestamp
89     * @param timestamp the timestamp value to include
90     */
91    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="MT_CORRECTNESS",
92        justification="Intentional")
93    void includeTimestamp(final long timestamp) {
94      long initialMinTimestamp = this.minimumTimestamp.get();
95      if (timestamp < initialMinTimestamp) {
96        long curMinTimestamp = initialMinTimestamp;
97        while (timestamp < curMinTimestamp) {
98          if (!this.minimumTimestamp.compareAndSet(curMinTimestamp, timestamp)) {
99            curMinTimestamp = this.minimumTimestamp.get();
100         } else {
101           // successfully set minimumTimestamp, break.
102           break;
103         }
104       }
105 
106       // When it reaches here, there are two possibilities:
107       //  1). timestamp >= curMinTimestamp, someone already sets the minimumTimestamp. In this case,
108       //      it still needs to check if initialMinTimestamp == INITIAL_MIN_TIMESTAMP to see
109       //      if it needs to update minimumTimestamp. Someone may already set both
110       //      minimumTimestamp/minimumTimestamp to the same value(curMinTimestamp),
111       //      need to check if maximumTimestamp needs to be updated.
112       //  2). timestamp < curMinTimestamp, it sets the minimumTimestamp successfully.
113       //      In this case,it still needs to check if initialMinTimestamp == INITIAL_MIN_TIMESTAMP
114       //      to see if it needs to set maximumTimestamp.
115       if (initialMinTimestamp != INITIAL_MIN_TIMESTAMP) {
116         // Someone already sets minimumTimestamp and timestamp is less than minimumTimestamp.
117         // In this case, no need to set maximumTimestamp as it will be set to at least
118         // initialMinTimestamp.
119         return;
120       }
121     }
122 
123     long curMaxTimestamp = this.maximumTimestamp.get();
124 
125     if (timestamp > curMaxTimestamp) {
126       while (timestamp > curMaxTimestamp) {
127         if (!this.maximumTimestamp.compareAndSet(curMaxTimestamp, timestamp)) {
128           curMaxTimestamp = this.maximumTimestamp.get();
129         } else {
130           // successfully set maximumTimestamp, break
131           break;
132         }
133       }
134     }
135   }
136 
137   /**
138    * Check if the range has ANY overlap with TimeRange
139    * @param tr TimeRange, it expects [minStamp, maxStamp)
140    * @return True if there is overlap, false otherwise
141    */
142   public boolean includesTimeRange(final TimeRange tr) {
143     return (this.minimumTimestamp.get() < tr.getMax() && this.maximumTimestamp.get() >= tr.getMin());
144   }
145 
146   /**
147    * @return the minimumTimestamp
148    */
149   public long getMin() {
150     return minimumTimestamp.get();
151   }
152 
153   /**
154    * @return the maximumTimestamp
155    */
156   public long getMax() {
157     return maximumTimestamp.get();
158   }
159 
160   @Override
161   public void write(final DataOutput out) throws IOException {
162     out.writeLong(minimumTimestamp.get());
163     out.writeLong(maximumTimestamp.get());
164   }
165 
166   @Override
167   public void readFields(final DataInput in) throws IOException {
168 
169     this.minimumTimestamp.set(in.readLong());
170     this.maximumTimestamp.set(in.readLong());
171   }
172 
173   @Override
174   public String toString() {
175     return "[" + minimumTimestamp.get() + "," + maximumTimestamp.get() + "]";
176   }
177 
178   /**
179    * @return An instance of TimeRangeTracker filled w/ the content of serialized
180    * TimeRangeTracker in <code>timeRangeTrackerBytes</code>.
181    * @throws IOException
182    */
183   public static TimeRangeTracker getTimeRangeTracker(final byte [] timeRangeTrackerBytes)
184   throws IOException {
185     if (timeRangeTrackerBytes == null) return null;
186     TimeRangeTracker trt = new TimeRangeTracker();
187     Writables.copyWritable(timeRangeTrackerBytes, trt);
188     return trt;
189   }
190 
191   /**
192    * @return An instance of a TimeRange made from the serialized TimeRangeTracker passed in
193    * <code>timeRangeTrackerBytes</code>.
194    * @throws IOException
195    */
196   static TimeRange getTimeRange(final byte [] timeRangeTrackerBytes) throws IOException {
197     TimeRangeTracker trt = getTimeRangeTracker(timeRangeTrackerBytes);
198     return trt == null? null: trt.toTimeRange();
199   }
200 
201   /**
202    * @return Make a TimeRange from current state of <code>this</code>.
203    */
204   TimeRange toTimeRange() throws IOException {
205     long min = getMin();
206     long max = getMax();
207     // Initial TimeRangeTracker timestamps are the opposite of what you want for a TimeRange. Fix!
208     if (min == INITIAL_MIN_TIMESTAMP) {
209       min = TimeRange.INITIAL_MIN_TIMESTAMP;
210     }
211     if (max == INITIAL_MAX_TIMESTAMP) {
212       max = TimeRange.INITIAL_MAX_TIMESTAMP;
213     }
214     return new TimeRange(min, max);
215   }
216 }