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