1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import java.util.LinkedList;
22
23 import org.apache.hadoop.hbase.classification.InterfaceAudience;
24 import org.apache.hadoop.hbase.util.Bytes;
25 import org.apache.hadoop.hbase.util.ClassSize;
26
27
28
29
30
31
32
33 @InterfaceAudience.Private
34 public class MultiVersionConsistencyControl {
35 private volatile long memstoreRead = 0;
36 private volatile long memstoreWrite = 0;
37
38 private final Object readWaiters = new Object();
39
40
41 private final LinkedList<WriteEntry> writeQueue =
42 new LinkedList<WriteEntry>();
43
44
45
46
47 public MultiVersionConsistencyControl() {
48 this.memstoreRead = this.memstoreWrite = 0;
49 }
50
51
52
53
54
55 public void initialize(long startPoint) {
56 synchronized (writeQueue) {
57 if (this.memstoreWrite != this.memstoreRead) {
58 throw new RuntimeException("Already used this mvcc. Too late to initialize");
59 }
60
61 this.memstoreRead = this.memstoreWrite = startPoint;
62 }
63 }
64
65
66
67
68
69
70 public WriteEntry beginMemstoreInsert() {
71 synchronized (writeQueue) {
72 long nextWriteNumber = ++memstoreWrite;
73 WriteEntry e = new WriteEntry(nextWriteNumber);
74 writeQueue.add(e);
75 return e;
76 }
77 }
78
79
80
81
82
83
84
85 public void completeMemstoreInsert(WriteEntry e) {
86 advanceMemstore(e);
87 waitForRead(e);
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101 boolean advanceMemstore(WriteEntry e) {
102 synchronized (writeQueue) {
103 e.markCompleted();
104
105 long nextReadValue = -1;
106 boolean ranOnce=false;
107 while (!writeQueue.isEmpty()) {
108 ranOnce=true;
109 WriteEntry queueFirst = writeQueue.getFirst();
110
111 if (nextReadValue > 0) {
112 if (nextReadValue+1 != queueFirst.getWriteNumber()) {
113 throw new RuntimeException("invariant in completeMemstoreInsert violated, prev: "
114 + nextReadValue + " next: " + queueFirst.getWriteNumber());
115 }
116 }
117
118 if (queueFirst.isCompleted()) {
119 nextReadValue = queueFirst.getWriteNumber();
120 writeQueue.removeFirst();
121 } else {
122 break;
123 }
124 }
125
126 if (!ranOnce) {
127 throw new RuntimeException("never was a first");
128 }
129
130 if (nextReadValue > 0) {
131 synchronized (readWaiters) {
132 memstoreRead = nextReadValue;
133 readWaiters.notifyAll();
134 }
135 }
136 if (memstoreRead >= e.getWriteNumber()) {
137 return true;
138 }
139 return false;
140 }
141 }
142
143
144
145
146
147 public void waitForRead(WriteEntry e) {
148 boolean interrupted = false;
149 synchronized (readWaiters) {
150 while (memstoreRead < e.getWriteNumber()) {
151 try {
152 readWaiters.wait(0);
153 } catch (InterruptedException ie) {
154
155
156 interrupted = true;
157 }
158 }
159 }
160 if (interrupted) Thread.currentThread().interrupt();
161 }
162
163 public long memstoreReadPoint() {
164 return memstoreRead;
165 }
166
167
168 public static class WriteEntry {
169 private long writeNumber;
170 private boolean completed = false;
171 WriteEntry(long writeNumber) {
172 this.writeNumber = writeNumber;
173 }
174 void markCompleted() {
175 this.completed = true;
176 }
177 boolean isCompleted() {
178 return this.completed;
179 }
180 long getWriteNumber() {
181 return this.writeNumber;
182 }
183 }
184
185 public static final long FIXED_SIZE = ClassSize.align(
186 ClassSize.OBJECT +
187 2 * Bytes.SIZEOF_LONG +
188 2 * ClassSize.REFERENCE);
189
190 }