1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.zookeeper.lock;
21
22 import java.io.IOException;
23 import java.util.Comparator;
24 import java.util.List;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.classification.InterfaceAudience;
32 import org.apache.hadoop.hbase.InterProcessLock;
33 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
34 import org.apache.hadoop.hbase.zookeeper.DeletionListener;
35 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
36 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
37 import org.apache.zookeeper.CreateMode;
38 import org.apache.zookeeper.KeeperException;
39 import org.apache.zookeeper.KeeperException.BadVersionException;
40 import org.apache.zookeeper.data.Stat;
41
42 import com.google.common.base.Preconditions;
43
44
45
46
47
48
49
50
51 @InterfaceAudience.Private
52 public abstract class ZKInterProcessLockBase implements InterProcessLock {
53
54 private static final Log LOG = LogFactory.getLog(ZKInterProcessLockBase.class);
55
56
57 protected static final String READ_LOCK_CHILD_NODE_PREFIX = "read-";
58
59
60 protected static final String WRITE_LOCK_CHILD_NODE_PREFIX = "write-";
61
62 protected final ZooKeeperWatcher zkWatcher;
63 protected final String parentLockNode;
64 protected final String fullyQualifiedZNode;
65 protected final byte[] metadata;
66 protected final MetadataHandler handler;
67
68
69 protected final AtomicReference<AcquiredLock> acquiredLock =
70 new AtomicReference<AcquiredLock>(null);
71
72
73
74
75 protected static class AcquiredLock {
76 private final String path;
77 private final int version;
78
79
80
81
82
83
84 public AcquiredLock(String path, int version) {
85 this.path = path;
86 this.version = version;
87 }
88
89 public String getPath() {
90 return path;
91 }
92
93 public int getVersion() {
94 return version;
95 }
96
97 @Override
98 public String toString() {
99 return "AcquiredLockInfo{" +
100 "path='" + path + '\'' +
101 ", version=" + version +
102 '}';
103 }
104 }
105
106 protected static class ZNodeComparator implements Comparator<String> {
107
108 public static final ZNodeComparator COMPARATOR = new ZNodeComparator();
109
110 private ZNodeComparator() {
111 }
112
113
114
115
116 public static int getChildSequenceId(String childZNode) {
117 Preconditions.checkNotNull(childZNode);
118 assert childZNode.length() >= 10;
119 String sequenceIdStr = childZNode.substring(childZNode.length() - 10);
120 return Integer.parseInt(sequenceIdStr);
121 }
122
123 @Override
124 public int compare(String zNode1, String zNode2) {
125 int seq1 = getChildSequenceId(zNode1);
126 int seq2 = getChildSequenceId(zNode2);
127 return seq1 - seq2;
128 }
129 }
130
131
132
133
134
135
136
137
138
139 protected ZKInterProcessLockBase(ZooKeeperWatcher zkWatcher,
140 String parentLockNode, byte[] metadata, MetadataHandler handler, String childNode) {
141 this.zkWatcher = zkWatcher;
142 this.parentLockNode = parentLockNode;
143 this.fullyQualifiedZNode = ZKUtil.joinZNode(parentLockNode, childNode);
144 this.metadata = metadata;
145 this.handler = handler;
146 }
147
148
149
150
151 @Override
152 public void acquire() throws IOException, InterruptedException {
153 tryAcquire(-1);
154 }
155
156
157
158
159 @Override
160 public boolean tryAcquire(long timeoutMs)
161 throws IOException, InterruptedException {
162 boolean hasTimeout = timeoutMs != -1;
163 long waitUntilMs =
164 hasTimeout ?EnvironmentEdgeManager.currentTimeMillis() + timeoutMs : -1;
165 String createdZNode;
166 try {
167 createdZNode = createLockZNode();
168 } catch (KeeperException ex) {
169 throw new IOException("Failed to create znode: " + fullyQualifiedZNode, ex);
170 }
171 while (true) {
172 List<String> children;
173 try {
174 children = ZKUtil.listChildrenNoWatch(zkWatcher, parentLockNode);
175 } catch (KeeperException e) {
176 LOG.error("Unexpected ZooKeeper error when listing children", e);
177 throw new IOException("Unexpected ZooKeeper exception", e);
178 }
179 String pathToWatch;
180 if ((pathToWatch = getLockPath(createdZNode, children)) == null) {
181 break;
182 }
183 CountDownLatch deletedLatch = new CountDownLatch(1);
184 String zkPathToWatch =
185 ZKUtil.joinZNode(parentLockNode, pathToWatch);
186 DeletionListener deletionListener =
187 new DeletionListener(zkWatcher, zkPathToWatch, deletedLatch);
188 zkWatcher.registerListener(deletionListener);
189 try {
190 if (ZKUtil.setWatchIfNodeExists(zkWatcher, zkPathToWatch)) {
191
192 if (hasTimeout) {
193 long remainingMs = waitUntilMs - EnvironmentEdgeManager.currentTimeMillis();
194 if (remainingMs < 0 ||
195 !deletedLatch.await(remainingMs, TimeUnit.MILLISECONDS)) {
196 LOG.warn("Unable to acquire the lock in " + timeoutMs +
197 " milliseconds.");
198 try {
199 ZKUtil.deleteNode(zkWatcher, createdZNode);
200 } catch (KeeperException e) {
201 LOG.warn("Unable to remove ZNode " + createdZNode);
202 }
203 return false;
204 }
205 } else {
206 deletedLatch.await();
207 }
208 if (deletionListener.hasException()) {
209 Throwable t = deletionListener.getException();
210 throw new IOException("Exception in the watcher", t);
211 }
212 }
213 } catch (KeeperException e) {
214 throw new IOException("Unexpected ZooKeeper exception", e);
215 } finally {
216 zkWatcher.unregisterListener(deletionListener);
217 }
218 }
219 updateAcquiredLock(createdZNode);
220 LOG.debug("Successfully acquired a lock for " + createdZNode);
221 return true;
222 }
223
224 private String createLockZNode() throws KeeperException {
225 try {
226 return ZKUtil.createNodeIfNotExistsNoWatch(zkWatcher, fullyQualifiedZNode,
227 metadata, CreateMode.EPHEMERAL_SEQUENTIAL);
228 } catch (KeeperException.NoNodeException nne) {
229
230 ZKUtil.createWithParents(zkWatcher, parentLockNode);
231 return createLockZNode();
232 }
233 }
234
235
236
237
238
239
240 protected static boolean isChildWriteLock(String child) {
241 int idx = child.lastIndexOf(ZKUtil.ZNODE_PATH_SEPARATOR);
242 String suffix = child.substring(idx + 1);
243 return suffix.startsWith(WRITE_LOCK_CHILD_NODE_PREFIX);
244 }
245
246
247
248
249
250
251 protected void updateAcquiredLock(String createdZNode) throws IOException {
252 Stat stat = new Stat();
253 byte[] data = null;
254 Exception ex = null;
255 try {
256 data = ZKUtil.getDataNoWatch(zkWatcher, createdZNode, stat);
257 } catch (KeeperException e) {
258 LOG.warn("Cannot getData for znode:" + createdZNode, e);
259 ex = e;
260 }
261 if (data == null) {
262 LOG.error("Can't acquire a lock on a non-existent node " + createdZNode);
263 throw new IllegalStateException("ZNode " + createdZNode +
264 "no longer exists!", ex);
265 }
266 AcquiredLock newLock = new AcquiredLock(createdZNode, stat.getVersion());
267 if (!acquiredLock.compareAndSet(null, newLock)) {
268 LOG.error("The lock " + fullyQualifiedZNode +
269 " has already been acquired by another process!");
270 throw new IllegalStateException(fullyQualifiedZNode +
271 " is held by another process");
272 }
273 }
274
275
276
277
278 @Override
279 public void release() throws IOException, InterruptedException {
280 AcquiredLock lock = acquiredLock.get();
281 if (lock == null) {
282 LOG.error("Cannot release lock" +
283 ", process does not have a lock for " + fullyQualifiedZNode);
284 throw new IllegalStateException("No lock held for " + fullyQualifiedZNode);
285 }
286 try {
287 if (ZKUtil.checkExists(zkWatcher, lock.getPath()) != -1) {
288 ZKUtil.deleteNode(zkWatcher, lock.getPath(), lock.getVersion());
289 if (!acquiredLock.compareAndSet(lock, null)) {
290 LOG.debug("Current process no longer holds " + lock + " for " +
291 fullyQualifiedZNode);
292 throw new IllegalStateException("Not holding a lock for " +
293 fullyQualifiedZNode +"!");
294 }
295 }
296 if (LOG.isDebugEnabled()) {
297 LOG.debug("Successfully released " + lock.getPath());
298 }
299 } catch (BadVersionException e) {
300 throw new IllegalStateException(e);
301 } catch (KeeperException e) {
302 throw new IOException(e);
303 }
304 }
305
306
307
308
309
310
311
312
313
314
315 protected boolean handleLockMetadata(String lockZNode)
316 throws IOException, InterruptedException {
317 byte[] metadata = null;
318 try {
319 metadata = ZKUtil.getData(zkWatcher, lockZNode);
320 } catch (KeeperException ex) {
321 LOG.warn("Cannot getData for znode:" + lockZNode, ex);
322 }
323 if (metadata == null) {
324 return false;
325 }
326 if (handler != null) {
327 handler.handleMetadata(metadata);
328 }
329 return true;
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 protected abstract String getLockPath(String myZNode, List<String> children)
346 throws IOException, InterruptedException;
347 }