1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.procedure2.store;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.Arrays;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.TreeMap;
28
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.hbase.classification.InterfaceStability;
31 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
32
33
34
35
36
37
38
39 @InterfaceAudience.Private
40 @InterfaceStability.Evolving
41 public class ProcedureStoreTracker {
42 private final TreeMap<Long, BitSetNode> map = new TreeMap<Long, BitSetNode>();
43
44 private boolean keepDeletes = false;
45 private boolean partial = false;
46
47 private long minUpdatedProcId = Long.MAX_VALUE;
48 private long maxUpdatedProcId = Long.MIN_VALUE;
49
50 public enum DeleteState { YES, NO, MAYBE }
51
52 public static class BitSetNode {
53 private final static long WORD_MASK = 0xffffffffffffffffL;
54 private final static int ADDRESS_BITS_PER_WORD = 6;
55 private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
56 private final static int MAX_NODE_SIZE = 1 << ADDRESS_BITS_PER_WORD;
57
58 private final boolean partial;
59 private long[] updated;
60 private long[] deleted;
61 private long start;
62
63 public void dump() {
64 System.out.printf("%06d:%06d min=%d max=%d%n", getStart(), getEnd(),
65 getMinProcId(), getMaxProcId());
66 System.out.println("Update:");
67 for (int i = 0; i < updated.length; ++i) {
68 for (int j = 0; j < BITS_PER_WORD; ++j) {
69 System.out.print((updated[i] & (1L << j)) != 0 ? "1" : "0");
70 }
71 System.out.println(" " + i);
72 }
73 System.out.println();
74 System.out.println("Delete:");
75 for (int i = 0; i < deleted.length; ++i) {
76 for (int j = 0; j < BITS_PER_WORD; ++j) {
77 System.out.print((deleted[i] & (1L << j)) != 0 ? "1" : "0");
78 }
79 System.out.println(" " + i);
80 }
81 System.out.println();
82 }
83
84 public BitSetNode(final long procId, final boolean partial) {
85 start = alignDown(procId);
86
87 int count = 1;
88 updated = new long[count];
89 deleted = new long[count];
90 for (int i = 0; i < count; ++i) {
91 updated[i] = 0;
92 deleted[i] = partial ? 0 : WORD_MASK;
93 }
94
95 this.partial = partial;
96 updateState(procId, false);
97 }
98
99 protected BitSetNode(final long start, final long[] updated, final long[] deleted) {
100 this.start = start;
101 this.updated = updated;
102 this.deleted = deleted;
103 this.partial = false;
104 }
105
106 public void update(final long procId) {
107 updateState(procId, false);
108 }
109
110 public void delete(final long procId) {
111 updateState(procId, true);
112 }
113
114 public Long getStart() {
115 return start;
116 }
117
118 public Long getEnd() {
119 return start + (updated.length << ADDRESS_BITS_PER_WORD) - 1;
120 }
121
122 public boolean contains(final long procId) {
123 return start <= procId && procId <= getEnd();
124 }
125
126 public DeleteState isDeleted(final long procId) {
127 int bitmapIndex = getBitmapIndex(procId);
128 int wordIndex = bitmapIndex >> ADDRESS_BITS_PER_WORD;
129 if (wordIndex >= deleted.length) {
130 return DeleteState.MAYBE;
131 }
132 return (deleted[wordIndex] & (1L << bitmapIndex)) != 0 ? DeleteState.YES : DeleteState.NO;
133 }
134
135 private boolean isUpdated(final long procId) {
136 int bitmapIndex = getBitmapIndex(procId);
137 int wordIndex = bitmapIndex >> ADDRESS_BITS_PER_WORD;
138 if (wordIndex >= updated.length) {
139 return false;
140 }
141 return (updated[wordIndex] & (1L << bitmapIndex)) != 0;
142 }
143
144 public boolean isUpdated() {
145
146 for (int i = 0; i < updated.length; ++i) {
147 if ((updated[i] | deleted[i]) != WORD_MASK) {
148 return false;
149 }
150 }
151 return true;
152 }
153
154 public boolean isEmpty() {
155
156 for (int i = 0; i < deleted.length; ++i) {
157 if (deleted[i] != WORD_MASK) {
158 return false;
159 }
160 }
161 return true;
162 }
163
164 public void resetUpdates() {
165 for (int i = 0; i < updated.length; ++i) {
166 updated[i] = 0;
167 }
168 }
169
170 public void undeleteAll() {
171 for (int i = 0; i < updated.length; ++i) {
172 deleted[i] = 0;
173 }
174 }
175
176 public void unsetPartialFlag() {
177 for (int i = 0; i < updated.length; ++i) {
178 for (int j = 0; j < BITS_PER_WORD; ++j) {
179 if ((updated[i] & (1L << j)) == 0) {
180 deleted[i] |= (1L << j);
181 }
182 }
183 }
184 }
185
186 public ProcedureProtos.ProcedureStoreTracker.TrackerNode convert() {
187 ProcedureProtos.ProcedureStoreTracker.TrackerNode.Builder builder =
188 ProcedureProtos.ProcedureStoreTracker.TrackerNode.newBuilder();
189 builder.setStartId(start);
190 for (int i = 0; i < updated.length; ++i) {
191 builder.addUpdated(updated[i]);
192 builder.addDeleted(deleted[i]);
193 }
194 return builder.build();
195 }
196
197 public static BitSetNode convert(ProcedureProtos.ProcedureStoreTracker.TrackerNode data) {
198 long start = data.getStartId();
199 int size = data.getUpdatedCount();
200 long[] updated = new long[size];
201 long[] deleted = new long[size];
202 for (int i = 0; i < size; ++i) {
203 updated[i] = data.getUpdated(i);
204 deleted[i] = data.getDeleted(i);
205 }
206 return new BitSetNode(start, updated, deleted);
207 }
208
209
210
211
212 public boolean canGrow(final long procId) {
213 return Math.abs(procId - start) < MAX_NODE_SIZE;
214 }
215
216 public boolean canMerge(final BitSetNode rightNode) {
217 assert start < rightNode.getEnd();
218 return (rightNode.getEnd() - start) < MAX_NODE_SIZE;
219 }
220
221 public void grow(final long procId) {
222 int delta, offset;
223
224 if (procId < start) {
225
226 long newStart = alignDown(procId);
227 delta = (int)(start - newStart) >> ADDRESS_BITS_PER_WORD;
228 offset = delta;
229 start = newStart;
230 } else {
231
232 long newEnd = alignUp(procId + 1);
233 delta = (int)(newEnd - getEnd()) >> ADDRESS_BITS_PER_WORD;
234 offset = 0;
235 }
236
237 long[] newBitmap;
238 int oldSize = updated.length;
239
240 newBitmap = new long[oldSize + delta];
241 for (int i = 0; i < newBitmap.length; ++i) {
242 newBitmap[i] = 0;
243 }
244 System.arraycopy(updated, 0, newBitmap, offset, oldSize);
245 updated = newBitmap;
246
247 newBitmap = new long[deleted.length + delta];
248 for (int i = 0; i < newBitmap.length; ++i) {
249 newBitmap[i] = partial ? 0 : WORD_MASK;
250 }
251 System.arraycopy(deleted, 0, newBitmap, offset, oldSize);
252 deleted = newBitmap;
253 }
254
255 public void merge(final BitSetNode rightNode) {
256 int delta = (int)(rightNode.getEnd() - getEnd()) >> ADDRESS_BITS_PER_WORD;
257
258 long[] newBitmap;
259 int oldSize = updated.length;
260 int newSize = (delta - rightNode.updated.length);
261 int offset = oldSize + newSize;
262
263 newBitmap = new long[oldSize + delta];
264 System.arraycopy(updated, 0, newBitmap, 0, oldSize);
265 System.arraycopy(rightNode.updated, 0, newBitmap, offset, rightNode.updated.length);
266 updated = newBitmap;
267
268 newBitmap = new long[oldSize + delta];
269 System.arraycopy(deleted, 0, newBitmap, 0, oldSize);
270 System.arraycopy(rightNode.deleted, 0, newBitmap, offset, rightNode.deleted.length);
271 deleted = newBitmap;
272
273 for (int i = 0; i < newSize; ++i) {
274 updated[offset + i] = 0;
275 deleted[offset + i] = partial ? 0 : WORD_MASK;
276 }
277 }
278
279 @Override
280 public String toString() {
281 return "BitSetNode(" + getStart() + "-" + getEnd() + ")";
282 }
283
284
285
286
287 public long getMinProcId() {
288 long minProcId = start;
289 for (int i = 0; i < deleted.length; ++i) {
290 if (deleted[i] == 0) {
291 return(minProcId);
292 }
293
294 if (deleted[i] != WORD_MASK) {
295 for (int j = 0; j < BITS_PER_WORD; ++j) {
296 if ((deleted[i] & (1L << j)) != 0) {
297 return minProcId + j;
298 }
299 }
300 }
301
302 minProcId += BITS_PER_WORD;
303 }
304 return minProcId;
305 }
306
307 public long getMaxProcId() {
308 long maxProcId = getEnd();
309 for (int i = deleted.length - 1; i >= 0; --i) {
310 if (deleted[i] == 0) {
311 return maxProcId;
312 }
313
314 if (deleted[i] != WORD_MASK) {
315 for (int j = BITS_PER_WORD - 1; j >= 0; --j) {
316 if ((deleted[i] & (1L << j)) == 0) {
317 return maxProcId - (BITS_PER_WORD - 1 - j);
318 }
319 }
320 }
321 maxProcId -= BITS_PER_WORD;
322 }
323 return maxProcId;
324 }
325
326
327
328
329 private int getBitmapIndex(final long procId) {
330 return (int)(procId - start);
331 }
332
333 private void updateState(final long procId, final boolean isDeleted) {
334 int bitmapIndex = getBitmapIndex(procId);
335 int wordIndex = bitmapIndex >> ADDRESS_BITS_PER_WORD;
336 long value = (1L << bitmapIndex);
337
338 if (isDeleted) {
339 updated[wordIndex] |= value;
340 deleted[wordIndex] |= value;
341 } else {
342 updated[wordIndex] |= value;
343 deleted[wordIndex] &= ~value;
344 }
345 }
346
347
348
349
350 private static long alignUp(final long x) {
351 return (x + (BITS_PER_WORD - 1)) & -BITS_PER_WORD;
352 }
353
354 private static long alignDown(final long x) {
355 return x & -BITS_PER_WORD;
356 }
357 }
358
359 public void insert(long procId) {
360 BitSetNode node = getOrCreateNode(procId);
361 node.update(procId);
362 trackProcIds(procId);
363 }
364
365 public void insert(final long procId, final long[] subProcIds) {
366 update(procId);
367 for (int i = 0; i < subProcIds.length; ++i) {
368 insert(subProcIds[i]);
369 }
370 }
371
372 public void update(long procId) {
373 Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId);
374 assert entry != null : "expected node to update procId=" + procId;
375
376 BitSetNode node = entry.getValue();
377 assert node.contains(procId);
378 node.update(procId);
379 trackProcIds(procId);
380 }
381
382 public void delete(long procId) {
383 Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId);
384 assert entry != null : "expected node to delete procId=" + procId;
385
386 BitSetNode node = entry.getValue();
387 assert node.contains(procId) : "expected procId in the node";
388 node.delete(procId);
389
390 if (!keepDeletes && node.isEmpty()) {
391
392 map.remove(entry.getKey());
393 }
394
395 trackProcIds(procId);
396 }
397
398 public void delete(long[] procIds) {
399
400 Arrays.sort(procIds);
401 for (int i = 0; i < procIds.length; ++i) {
402 delete(procIds[i]);
403 }
404 }
405
406 private void trackProcIds(long procId) {
407 minUpdatedProcId = Math.min(minUpdatedProcId, procId);
408 maxUpdatedProcId = Math.max(maxUpdatedProcId, procId);
409 }
410
411 public long getUpdatedMinProcId() {
412 return minUpdatedProcId;
413 }
414
415 public long getUpdatedMaxProcId() {
416 return maxUpdatedProcId;
417 }
418
419 @InterfaceAudience.Private
420 public void setDeleted(final long procId, final boolean isDeleted) {
421 BitSetNode node = getOrCreateNode(procId);
422 assert node.contains(procId) : "expected procId=" + procId + " in the node=" + node;
423 node.updateState(procId, isDeleted);
424 }
425
426 public void reset() {
427 this.keepDeletes = false;
428 this.partial = false;
429 this.map.clear();
430 resetUpdates();
431 }
432
433 public DeleteState isDeleted(long procId) {
434 Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId);
435 if (entry != null && entry.getValue().contains(procId)) {
436 BitSetNode node = entry.getValue();
437 DeleteState state = node.isDeleted(procId);
438 return partial && !node.isUpdated(procId) ? DeleteState.MAYBE : state;
439 }
440 return partial ? DeleteState.MAYBE : DeleteState.YES;
441 }
442
443 public long getMinProcId() {
444
445 Map.Entry<Long, BitSetNode> entry = map.firstEntry();
446 return entry == null ? 0 : entry.getValue().getMinProcId();
447 }
448
449 public void setKeepDeletes(boolean keepDeletes) {
450 this.keepDeletes = keepDeletes;
451 if (!keepDeletes) {
452 Iterator<Map.Entry<Long, BitSetNode>> it = map.entrySet().iterator();
453 while (it.hasNext()) {
454 Map.Entry<Long, BitSetNode> entry = it.next();
455 if (entry.getValue().isEmpty()) {
456 it.remove();
457 }
458 }
459 }
460 }
461
462 public void setPartialFlag(boolean isPartial) {
463 if (this.partial && !isPartial) {
464 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
465 entry.getValue().unsetPartialFlag();
466 }
467 }
468 this.partial = isPartial;
469 }
470
471 public boolean isEmpty() {
472 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
473 if (entry.getValue().isEmpty() == false) {
474 return false;
475 }
476 }
477 return true;
478 }
479
480 public boolean isUpdated() {
481 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
482 if (entry.getValue().isUpdated() == false) {
483 return false;
484 }
485 }
486 return true;
487 }
488
489 public boolean isTracking(long minId, long maxId) {
490
491 return map.floorEntry(minId) != null || map.floorEntry(maxId) != null;
492 }
493
494 public void resetUpdates() {
495 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
496 entry.getValue().resetUpdates();
497 }
498 minUpdatedProcId = Long.MAX_VALUE;
499 maxUpdatedProcId = Long.MIN_VALUE;
500 }
501
502 public void undeleteAll() {
503 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
504 entry.getValue().undeleteAll();
505 }
506 }
507
508 private BitSetNode getOrCreateNode(final long procId) {
509
510 BitSetNode leftNode = null;
511 boolean leftCanGrow = false;
512 Map.Entry<Long, BitSetNode> leftEntry = map.floorEntry(procId);
513 if (leftEntry != null) {
514 leftNode = leftEntry.getValue();
515 if (leftNode.contains(procId)) {
516 return leftNode;
517 }
518 leftCanGrow = leftNode.canGrow(procId);
519 }
520
521 BitSetNode rightNode = null;
522 boolean rightCanGrow = false;
523 Map.Entry<Long, BitSetNode> rightEntry = map.ceilingEntry(procId);
524 if (rightEntry != null) {
525 rightNode = rightEntry.getValue();
526 rightCanGrow = rightNode.canGrow(procId);
527 if (leftNode != null) {
528 if (leftNode.canMerge(rightNode)) {
529
530 return mergeNodes(leftNode, rightNode);
531 }
532
533 if (leftCanGrow && rightCanGrow) {
534 if ((procId - leftNode.getEnd()) <= (rightNode.getStart() - procId)) {
535
536 return growNode(leftNode, procId);
537 }
538
539 return growNode(rightNode, procId);
540 }
541 }
542 }
543
544
545 if (leftCanGrow) {
546 return growNode(leftNode, procId);
547 }
548
549
550 if (rightCanGrow) {
551 return growNode(rightNode, procId);
552 }
553
554
555 BitSetNode node = new BitSetNode(procId, partial);
556 map.put(node.getStart(), node);
557 return node;
558 }
559
560 private BitSetNode growNode(BitSetNode node, long procId) {
561 map.remove(node.getStart());
562 node.grow(procId);
563 map.put(node.getStart(), node);
564 return node;
565 }
566
567 private BitSetNode mergeNodes(BitSetNode leftNode, BitSetNode rightNode) {
568 assert leftNode.getStart() < rightNode.getStart();
569 leftNode.merge(rightNode);
570 map.remove(rightNode.getStart());
571 return leftNode;
572 }
573
574 public void dump() {
575 System.out.println("map " + map.size());
576 System.out.println("isUpdated " + isUpdated());
577 System.out.println("isEmpty " + isEmpty());
578 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
579 entry.getValue().dump();
580 }
581 }
582
583 public void writeTo(final OutputStream stream) throws IOException {
584 ProcedureProtos.ProcedureStoreTracker.Builder builder =
585 ProcedureProtos.ProcedureStoreTracker.newBuilder();
586 for (Map.Entry<Long, BitSetNode> entry : map.entrySet()) {
587 builder.addNode(entry.getValue().convert());
588 }
589 builder.build().writeDelimitedTo(stream);
590 }
591
592 public void readFrom(final InputStream stream) throws IOException {
593 reset();
594 final ProcedureProtos.ProcedureStoreTracker data =
595 ProcedureProtos.ProcedureStoreTracker.parseDelimitedFrom(stream);
596 for (ProcedureProtos.ProcedureStoreTracker.TrackerNode protoNode: data.getNodeList()) {
597 final BitSetNode node = BitSetNode.convert(protoNode);
598 map.put(node.getStart(), node);
599 }
600 }
601 }