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 }