View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import java.util.LinkedList;
23  import java.util.concurrent.atomic.AtomicLong;
24  
25  /**
26   * Manages the read/write consistency within memstore. This provides
27   * an interface for readers to determine what entries to ignore, and
28   * a mechanism for writers to obtain new write numbers, then "commit"
29   * the new writes for readers to read (thus forming atomic transactions).
30   */
31  public class ReadWriteConsistencyControl {
32    private volatile long memstoreRead = 0;
33    private volatile long memstoreWrite = 0;
34  
35    private final Object readWaiters = new Object();
36  
37    // This is the pending queue of writes.
38    private final LinkedList<WriteEntry> writeQueue =
39        new LinkedList<WriteEntry>();
40  
41    private static final ThreadLocal<Long> perThreadReadPoint =
42        new ThreadLocal<Long>();
43  
44    /**
45     * Get this thread's read point. Used primarily by the memstore scanner to
46     * know which values to skip (ie: have not been completed/committed to 
47     * memstore).
48     */
49    public static long getThreadReadPoint() {
50      return perThreadReadPoint.get();
51    }
52  
53    /** 
54     * Set the thread read point to the given value. The thread RWCC
55     * is used by the Memstore scanner so it knows which values to skip. 
56     * Give it a value of 0 if you want everything.
57     */
58    public static void setThreadReadPoint(long readPoint) {
59      perThreadReadPoint.set(readPoint);
60    }
61  
62    /**
63     * Set the thread RWCC read point to whatever the current read point is in
64     * this particular instance of RWCC.  Returns the new thread read point value.
65     */
66    public static long resetThreadReadPoint(ReadWriteConsistencyControl rwcc) {
67      perThreadReadPoint.set(rwcc.memstoreReadPoint());
68      return getThreadReadPoint();
69    }
70    
71    /**
72     * Set the thread RWCC read point to 0 (include everything).
73     */
74    public static void resetThreadReadPoint() {
75      perThreadReadPoint.set(0L);
76    }
77  
78    public WriteEntry beginMemstoreInsert() {
79      synchronized (writeQueue) {
80        long nextWriteNumber = ++memstoreWrite;
81        WriteEntry e = new WriteEntry(nextWriteNumber);
82        writeQueue.add(e);
83        return e;
84      }
85    }
86  
87    public void completeMemstoreInsert(WriteEntry e) {
88      synchronized (writeQueue) {
89        e.markCompleted();
90  
91        long nextReadValue = -1;
92        boolean ranOnce=false;
93        while (!writeQueue.isEmpty()) {
94          ranOnce=true;
95          WriteEntry queueFirst = writeQueue.getFirst();
96  
97          if (nextReadValue > 0) {
98            if (nextReadValue+1 != queueFirst.getWriteNumber()) {
99              throw new RuntimeException("invariant in completeMemstoreInsert violated, prev: "
100                 + nextReadValue + " next: " + queueFirst.getWriteNumber());
101           }
102         }
103 
104         if (queueFirst.isCompleted()) {
105           nextReadValue = queueFirst.getWriteNumber();
106           writeQueue.removeFirst();
107         } else {
108           break;
109         }
110       }
111 
112       if (!ranOnce) {
113         throw new RuntimeException("never was a first");
114       }
115 
116       if (nextReadValue > 0) {
117         memstoreRead = nextReadValue;
118 
119         synchronized (readWaiters) {
120           readWaiters.notifyAll();
121         }
122 
123       }
124     }
125 
126     boolean interrupted = false;
127     while (memstoreRead < e.getWriteNumber()) {
128       synchronized (readWaiters) {
129         try {
130           readWaiters.wait(0);
131         } catch (InterruptedException ie) {
132           // We were interrupted... finish the loop -- i.e. cleanup --and then
133           // on our way out, reset the interrupt flag.
134           interrupted = true;
135         }
136       }
137     }
138     if (interrupted) Thread.currentThread().interrupt();
139   }
140 
141   public long memstoreReadPoint() {
142     return memstoreRead;
143   }
144 
145 
146   public static class WriteEntry {
147     private long writeNumber;
148     private boolean completed = false;
149     WriteEntry(long writeNumber) {
150       this.writeNumber = writeNumber;
151     }
152     void markCompleted() {
153       this.completed = true;
154     }
155     boolean isCompleted() {
156       return this.completed;
157     }
158     long getWriteNumber() {
159       return this.writeNumber;
160     }
161   }
162 }