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.wal;
20
21 import com.google.protobuf.InvalidProtocolBufferException;
22
23 import java.io.IOException;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.hadoop.fs.FSDataInputStream;
28 import org.apache.hadoop.hbase.ProcedureInfo;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.hbase.classification.InterfaceStability;
31 import org.apache.hadoop.hbase.procedure2.Procedure;
32 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore.ProcedureIterator;
33 import org.apache.hadoop.hbase.procedure2.store.ProcedureStoreTracker;
34 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
35 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos.ProcedureWALEntry;
36
37
38
39
40 @InterfaceAudience.Private
41 @InterfaceStability.Evolving
42 public class ProcedureWALFormatReader {
43 private static final Log LOG = LogFactory.getLog(ProcedureWALFormatReader.class);
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 private final WalProcedureMap localProcedureMap = new WalProcedureMap(1024);
101 private final WalProcedureMap procedureMap = new WalProcedureMap(1024);
102
103
104 private long maxProcId = 0;
105
106 private final ProcedureStoreTracker tracker;
107 private final boolean hasFastStartSupport;
108
109 public ProcedureWALFormatReader(final ProcedureStoreTracker tracker) {
110 this.tracker = tracker;
111
112 this.hasFastStartSupport = !tracker.isEmpty();
113 }
114
115 public void read(ProcedureWALFile log, ProcedureWALFormat.Loader loader) throws IOException {
116 long count = 0;
117 FSDataInputStream stream = log.getStream();
118 try {
119 boolean hasMore = true;
120 while (hasMore) {
121 ProcedureWALEntry entry = ProcedureWALFormat.readEntry(stream);
122 if (entry == null) {
123 LOG.warn("nothing left to decode. exiting with missing EOF");
124 hasMore = false;
125 break;
126 }
127 count++;
128 switch (entry.getType()) {
129 case PROCEDURE_WAL_INIT:
130 readInitEntry(entry);
131 break;
132 case PROCEDURE_WAL_INSERT:
133 readInsertEntry(entry);
134 break;
135 case PROCEDURE_WAL_UPDATE:
136 case PROCEDURE_WAL_COMPACT:
137 readUpdateEntry(entry);
138 break;
139 case PROCEDURE_WAL_DELETE:
140 readDeleteEntry(entry);
141 break;
142 case PROCEDURE_WAL_EOF:
143 hasMore = false;
144 break;
145 default:
146 throw new CorruptedWALProcedureStoreException("Invalid entry: " + entry);
147 }
148 }
149 LOG.info("Read " + count + " entries in " + log);
150 } catch (InvalidProtocolBufferException e) {
151 LOG.error("While reading entry #" + count + " in " + log, e);
152 loader.markCorruptedWAL(log, e);
153 }
154
155 if (!localProcedureMap.isEmpty()) {
156 log.setProcIds(localProcedureMap.getMinProcId(), localProcedureMap.getMaxProcId());
157 procedureMap.mergeTail(localProcedureMap);
158
159
160
161
162
163
164
165
166 }
167 }
168
169 public void finalize(ProcedureWALFormat.Loader loader) throws IOException {
170
171 loader.setMaxProcId(maxProcId);
172
173
174 ProcedureIterator procIter = procedureMap.fetchReady();
175 if (procIter != null) loader.load(procIter);
176
177
178
179 procIter = procedureMap.fetchAll();
180 if (procIter != null) loader.handleCorrupted(procIter);
181 }
182
183 private void loadProcedure(final ProcedureWALEntry entry, final ProcedureProtos.Procedure proc) {
184 maxProcId = Math.max(maxProcId, proc.getProcId());
185 if (isRequired(proc.getProcId())) {
186 if (LOG.isTraceEnabled()) {
187 LOG.trace("read " + entry.getType() + " entry " + proc.getProcId());
188 }
189 localProcedureMap.add(proc);
190 tracker.setDeleted(proc.getProcId(), false);
191 }
192 }
193
194 private void readInitEntry(final ProcedureWALEntry entry)
195 throws IOException {
196 assert entry.getProcedureCount() == 1 : "Expected only one procedure";
197 loadProcedure(entry, entry.getProcedure(0));
198 }
199
200 private void readInsertEntry(final ProcedureWALEntry entry) throws IOException {
201 assert entry.getProcedureCount() >= 1 : "Expected one or more procedures";
202 loadProcedure(entry, entry.getProcedure(0));
203 for (int i = 1; i < entry.getProcedureCount(); ++i) {
204 loadProcedure(entry, entry.getProcedure(i));
205 }
206 }
207
208 private void readUpdateEntry(final ProcedureWALEntry entry) throws IOException {
209 assert entry.getProcedureCount() == 1 : "Expected only one procedure";
210 loadProcedure(entry, entry.getProcedure(0));
211 }
212
213 private void readDeleteEntry(final ProcedureWALEntry entry) throws IOException {
214 assert entry.hasProcId() : "expected ProcID";
215
216 if (entry.getChildIdCount() > 0) {
217 assert entry.getProcedureCount() == 1 : "Expected only one procedure";
218
219
220 loadProcedure(entry, entry.getProcedure(0));
221
222
223 for (int i = 0, count = entry.getChildIdCount(); i < count; ++i) {
224 deleteEntry(entry.getChildId(i));
225 }
226 } else {
227 assert entry.getProcedureCount() == 0 : "Expected no procedures";
228
229
230 deleteEntry(entry.getProcId());
231 }
232 }
233
234 private void deleteEntry(final long procId) {
235 if (LOG.isTraceEnabled()) {
236 LOG.trace("delete entry " + procId);
237 }
238 maxProcId = Math.max(maxProcId, procId);
239 localProcedureMap.remove(procId);
240 assert !procedureMap.contains(procId);
241 tracker.setDeleted(procId, true);
242 }
243
244 private boolean isDeleted(final long procId) {
245 return tracker.isDeleted(procId) == ProcedureStoreTracker.DeleteState.YES;
246 }
247
248 private boolean isRequired(final long procId) {
249 return !isDeleted(procId) && !procedureMap.contains(procId);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 private static class Entry {
269
270 protected Entry hashNext;
271
272 protected Entry childHead;
273
274 protected Entry linkNext;
275 protected Entry linkPrev;
276
277 protected Entry replayNext;
278 protected Entry replayPrev;
279
280 protected Procedure procedure;
281 protected ProcedureProtos.Procedure proto;
282 protected boolean ready = false;
283
284 public Entry(Entry hashNext) { this.hashNext = hashNext; }
285
286 public long getProcId() { return proto.getProcId(); }
287 public long getParentId() { return proto.getParentId(); }
288 public boolean hasParent() { return proto.hasParentId(); }
289 public boolean isReady() { return ready; }
290
291 public boolean isCompleted() {
292 if (!hasParent()) {
293
294
295 switch (proto.getState()) {
296 case ROLLEDBACK:
297 return true;
298 case FINISHED:
299 return !proto.hasException();
300 default:
301 break;
302 }
303 }
304 return false;
305 }
306
307 public Procedure convert() throws IOException {
308 if (procedure == null) {
309 procedure = Procedure.convert(proto);
310 }
311 return procedure;
312 }
313
314 public ProcedureInfo convertToInfo() {
315 return ProcedureInfo.convert(proto);
316 }
317
318 @Override
319 public String toString() {
320 final StringBuilder sb = new StringBuilder();
321 sb.append("Entry(");
322 sb.append(getProcId());
323 sb.append(", parentId=");
324 sb.append(getParentId());
325 sb.append(", class=");
326 sb.append(proto.getClassName());
327 sb.append(")");
328 return sb.toString();
329 }
330 }
331
332 private static class EntryIterator implements ProcedureIterator {
333 private final Entry replayHead;
334 private Entry current;
335
336 public EntryIterator(Entry replayHead) {
337 this.replayHead = replayHead;
338 this.current = replayHead;
339 }
340
341 @Override
342 public void reset() {
343 this.current = replayHead;
344 }
345
346 @Override
347 public boolean hasNext() {
348 return current != null;
349 }
350
351 @Override
352 public boolean isNextCompleted() {
353 return current != null && current.isCompleted();
354 }
355
356 @Override
357 public void skipNext() {
358 current = current.replayNext;
359 }
360
361 @Override
362 public Procedure nextAsProcedure() throws IOException {
363 try {
364 return current.convert();
365 } finally {
366 current = current.replayNext;
367 }
368 }
369
370 @Override
371 public ProcedureInfo nextAsProcedureInfo() {
372 try {
373 return current.convertToInfo();
374 } finally {
375 current = current.replayNext;
376 }
377 }
378 }
379
380 private static class WalProcedureMap {
381
382 private Entry[] procedureMap;
383
384
385 private Entry replayOrderHead;
386 private Entry replayOrderTail;
387
388
389 private Entry rootHead;
390
391
392 private Entry childUnlinkedHead;
393
394
395 private long minProcId = Long.MAX_VALUE;
396 private long maxProcId = Long.MIN_VALUE;
397
398 public WalProcedureMap(int size) {
399 procedureMap = new Entry[size];
400 replayOrderHead = null;
401 replayOrderTail = null;
402 rootHead = null;
403 childUnlinkedHead = null;
404 }
405
406 public void add(ProcedureProtos.Procedure procProto) {
407 trackProcIds(procProto.getProcId());
408 Entry entry = addToMap(procProto.getProcId(), procProto.hasParentId());
409 boolean isNew = entry.proto == null;
410 entry.proto = procProto;
411 addToReplayList(entry);
412
413 if (isNew) {
414 if (procProto.hasParentId()) {
415 childUnlinkedHead = addToLinkList(entry, childUnlinkedHead);
416 } else {
417 rootHead = addToLinkList(entry, rootHead);
418 }
419 }
420 }
421
422 public boolean remove(long procId) {
423 trackProcIds(procId);
424 Entry entry = removeFromMap(procId);
425 if (entry != null) {
426 unlinkFromReplayList(entry);
427 unlinkFromLinkList(entry);
428 return true;
429 }
430 return false;
431 }
432
433 private void trackProcIds(long procId) {
434 minProcId = Math.min(minProcId, procId);
435 maxProcId = Math.max(maxProcId, procId);
436 }
437
438 public long getMinProcId() {
439 return minProcId;
440 }
441
442 public long getMaxProcId() {
443 return maxProcId;
444 }
445
446 public boolean contains(long procId) {
447 return getProcedure(procId) != null;
448 }
449
450 public boolean isEmpty() {
451 return replayOrderHead == null;
452 }
453
454 public void clear() {
455 for (int i = 0; i < procedureMap.length; ++i) {
456 procedureMap[i] = null;
457 }
458 replayOrderHead = null;
459 replayOrderTail = null;
460 rootHead = null;
461 childUnlinkedHead = null;
462 minProcId = Long.MAX_VALUE;
463 maxProcId = Long.MIN_VALUE;
464 }
465
466
467
468
469
470
471
472
473
474
475
476 public void mergeTail(WalProcedureMap other) {
477 for (Entry p = other.replayOrderHead; p != null; p = p.replayNext) {
478 int slotIndex = getMapSlot(p.getProcId());
479 p.hashNext = procedureMap[slotIndex];
480 procedureMap[slotIndex] = p;
481 }
482
483 if (replayOrderHead == null) {
484 replayOrderHead = other.replayOrderHead;
485 replayOrderTail = other.replayOrderTail;
486 rootHead = other.rootHead;
487 childUnlinkedHead = other.childUnlinkedHead;
488 } else {
489
490 assert replayOrderTail.replayNext == null;
491 assert other.replayOrderHead.replayPrev == null;
492 replayOrderTail.replayNext = other.replayOrderHead;
493 other.replayOrderHead.replayPrev = replayOrderTail;
494 replayOrderTail = other.replayOrderTail;
495
496
497 if (rootHead == null) {
498 rootHead = other.rootHead;
499 } else if (other.rootHead != null) {
500 Entry otherTail = findLinkListTail(other.rootHead);
501 otherTail.linkNext = rootHead;
502 rootHead.linkPrev = otherTail;
503 rootHead = other.rootHead;
504 }
505
506
507 if (childUnlinkedHead == null) {
508 childUnlinkedHead = other.childUnlinkedHead;
509 } else if (other.childUnlinkedHead != null) {
510 Entry otherTail = findLinkListTail(other.childUnlinkedHead);
511 otherTail.linkNext = childUnlinkedHead;
512 childUnlinkedHead.linkPrev = otherTail;
513 childUnlinkedHead = other.childUnlinkedHead;
514 }
515 }
516
517 other.clear();
518 }
519
520
521
522
523
524
525 public EntryIterator fetchReady() {
526 buildGraph();
527
528 Entry readyHead = null;
529 Entry readyTail = null;
530 Entry p = replayOrderHead;
531 while (p != null) {
532 Entry next = p.replayNext;
533 if (p.isReady()) {
534 unlinkFromReplayList(p);
535 if (readyTail != null) {
536 readyTail.replayNext = p;
537 p.replayPrev = readyTail;
538 } else {
539 p.replayPrev = null;
540 readyHead = p;
541 }
542 readyTail = p;
543 p.replayNext = null;
544 }
545 p = next;
546 }
547
548
549 for (p = readyHead; p != null; p = p.replayNext) {
550 removeFromMap(p.getProcId());
551 unlinkFromLinkList(p);
552 }
553 return readyHead != null ? new EntryIterator(readyHead) : null;
554 }
555
556
557
558
559 public EntryIterator fetchAll() {
560 Entry head = replayOrderHead;
561 for (Entry p = head; p != null; p = p.replayNext) {
562 removeFromMap(p.getProcId());
563 }
564 for (int i = 0; i < procedureMap.length; ++i) {
565 assert procedureMap[i] == null : "map not empty i=" + i;
566 }
567 replayOrderHead = null;
568 replayOrderTail = null;
569 childUnlinkedHead = null;
570 rootHead = null;
571 return head != null ? new EntryIterator(head) : null;
572 }
573
574 private void buildGraph() {
575 Entry p = childUnlinkedHead;
576 while (p != null) {
577 Entry next = p.linkNext;
578 Entry rootProc = getRootProcedure(p);
579 if (rootProc != null) {
580 rootProc.childHead = addToLinkList(p, rootProc.childHead);
581 }
582 p = next;
583 }
584
585 for (p = rootHead; p != null; p = p.linkNext) {
586 checkReadyToRun(p);
587 }
588 }
589
590 private Entry getRootProcedure(Entry entry) {
591 while (entry != null && entry.hasParent()) {
592 entry = getProcedure(entry.getParentId());
593 }
594 return entry;
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636 private boolean checkReadyToRun(Entry rootEntry) {
637 assert !rootEntry.hasParent() : "expected root procedure, got " + rootEntry;
638
639 if (rootEntry.isCompleted()) {
640
641 if (rootEntry.childHead != null) {
642 LOG.error("unexpected active children for root-procedure: " + rootEntry);
643 for (Entry p = rootEntry.childHead; p != null; p = p.linkNext) {
644 LOG.error("unexpected active children: " + p);
645 }
646 }
647
648 assert rootEntry.childHead == null : "unexpected children on root completion. " + rootEntry;
649 rootEntry.ready = true;
650 return true;
651 }
652
653 int stackIdSum = 0;
654 int maxStackId = 0;
655 for (int i = 0; i < rootEntry.proto.getStackIdCount(); ++i) {
656 int stackId = 1 + rootEntry.proto.getStackId(i);
657 maxStackId = Math.max(maxStackId, stackId);
658 stackIdSum += stackId;
659 }
660
661 for (Entry p = rootEntry.childHead; p != null; p = p.linkNext) {
662 for (int i = 0; i < p.proto.getStackIdCount(); ++i) {
663 int stackId = 1 + p.proto.getStackId(i);
664 maxStackId = Math.max(maxStackId, stackId);
665 stackIdSum += stackId;
666 }
667 }
668 final int cmpStackIdSum = (maxStackId * (maxStackId + 1) / 2);
669 if (cmpStackIdSum == stackIdSum) {
670 rootEntry.ready = true;
671 for (Entry p = rootEntry.childHead; p != null; p = p.linkNext) {
672 p.ready = true;
673 }
674 return true;
675 }
676 return false;
677 }
678
679 private void unlinkFromReplayList(Entry entry) {
680 if (replayOrderHead == entry) {
681 replayOrderHead = entry.replayNext;
682 }
683 if (replayOrderTail == entry) {
684 replayOrderTail = entry.replayPrev;
685 }
686 if (entry.replayPrev != null) {
687 entry.replayPrev.replayNext = entry.replayNext;
688 }
689 if (entry.replayNext != null) {
690 entry.replayNext.replayPrev = entry.replayPrev;
691 }
692 }
693
694 private void addToReplayList(final Entry entry) {
695 unlinkFromReplayList(entry);
696 entry.replayNext = replayOrderHead;
697 entry.replayPrev = null;
698 if (replayOrderHead != null) {
699 replayOrderHead.replayPrev = entry;
700 } else {
701 replayOrderTail = entry;
702 }
703 replayOrderHead = entry;
704 }
705
706 private void unlinkFromLinkList(Entry entry) {
707 if (entry == rootHead) {
708 rootHead = entry.linkNext;
709 } else if (entry == childUnlinkedHead) {
710 childUnlinkedHead = entry.linkNext;
711 }
712 if (entry.linkPrev != null) {
713 entry.linkPrev.linkNext = entry.linkNext;
714 }
715 if (entry.linkNext != null) {
716 entry.linkNext.linkPrev = entry.linkPrev;
717 }
718 }
719
720 private Entry addToLinkList(Entry entry, Entry linkHead) {
721 unlinkFromLinkList(entry);
722 entry.linkNext = linkHead;
723 entry.linkPrev = null;
724 if (linkHead != null) {
725 linkHead.linkPrev = entry;
726 }
727 return entry;
728 }
729
730 private Entry findLinkListTail(Entry linkHead) {
731 Entry tail = linkHead;
732 while (tail.linkNext != null) {
733 tail = tail.linkNext;
734 }
735 return tail;
736 }
737
738 private Entry addToMap(final long procId, final boolean hasParent) {
739 int slotIndex = getMapSlot(procId);
740 Entry entry = getProcedure(slotIndex, procId);
741 if (entry != null) return entry;
742
743 entry = new Entry(procedureMap[slotIndex]);
744 procedureMap[slotIndex] = entry;
745 return entry;
746 }
747
748 private Entry removeFromMap(final long procId) {
749 int slotIndex = getMapSlot(procId);
750 Entry prev = null;
751 Entry entry = procedureMap[slotIndex];
752 while (entry != null) {
753 if (procId == entry.getProcId()) {
754 if (prev != null) {
755 prev.hashNext = entry.hashNext;
756 } else {
757 procedureMap[slotIndex] = entry.hashNext;
758 }
759 entry.hashNext = null;
760 return entry;
761 }
762 prev = entry;
763 entry = entry.hashNext;
764 }
765 return null;
766 }
767
768 private Entry getProcedure(final long procId) {
769 return getProcedure(getMapSlot(procId), procId);
770 }
771
772 private Entry getProcedure(final int slotIndex, final long procId) {
773 Entry entry = procedureMap[slotIndex];
774 while (entry != null) {
775 if (procId == entry.getProcId()) {
776 return entry;
777 }
778 entry = entry.hashNext;
779 }
780 return null;
781 }
782
783 private int getMapSlot(final long procId) {
784 return (int)(Procedure.getProcIdHashCode(procId) % procedureMap.length);
785 }
786 }
787 }