blob: 74bff8b25b85a8566e847fe020213d505f3afe86 [file] [log] [blame]
Madan Jampani12390c12014-11-12 00:35:56 -08001package org.onlab.onos.store.service.impl;
2
Madan Jampania88d1f52014-11-14 16:45:24 -08003import static org.slf4j.LoggerFactory.getLogger;
4
Ray Milkey241b96a2014-11-17 13:08:20 -08005import java.nio.charset.StandardCharsets;
Madan Jampani12390c12014-11-12 00:35:56 -08006import java.util.UUID;
7import java.util.concurrent.CompletableFuture;
8import java.util.concurrent.ExecutionException;
9import java.util.concurrent.TimeUnit;
10import java.util.concurrent.TimeoutException;
11import java.util.concurrent.atomic.AtomicBoolean;
12
13import org.joda.time.DateTime;
14import org.onlab.onos.cluster.ClusterService;
Madan Jampani71582ed2014-11-18 10:06:01 -080015import org.onlab.onos.store.service.DatabaseException;
Madan Jampani12390c12014-11-12 00:35:56 -080016import org.onlab.onos.store.service.DatabaseService;
17import org.onlab.onos.store.service.Lock;
Madan Jampania88d1f52014-11-14 16:45:24 -080018import org.slf4j.Logger;
Madan Jampani12390c12014-11-12 00:35:56 -080019
20/**
21 * A distributed lock implementation.
22 */
23public class DistributedLock implements Lock {
24
Madan Jampania88d1f52014-11-14 16:45:24 -080025 private final Logger log = getLogger(getClass());
26
Madan Jampani12390c12014-11-12 00:35:56 -080027 private final DistributedLockManager lockManager;
28 private final DatabaseService databaseService;
29 private final String path;
30 private DateTime lockExpirationTime;
31 private AtomicBoolean isLocked = new AtomicBoolean(false);
32 private byte[] lockId;
33
34 public DistributedLock(
35 String path,
36 DatabaseService databaseService,
37 ClusterService clusterService,
38 DistributedLockManager lockManager) {
39
40 this.path = path;
41 this.databaseService = databaseService;
42 this.lockManager = lockManager;
43 this.lockId =
Ray Milkey241b96a2014-11-17 13:08:20 -080044 (UUID.randomUUID().toString() + "::" +
45 clusterService.getLocalNode().id().toString()).
46 getBytes(StandardCharsets.UTF_8);
Madan Jampani12390c12014-11-12 00:35:56 -080047 }
48
49 @Override
50 public String path() {
51 return path;
52 }
53
54 @Override
Madan Jampani71582ed2014-11-18 10:06:01 -080055 public void lock(int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -080056 try {
57 lockAsync(leaseDurationMillis).get();
58 } catch (ExecutionException e) {
59 throw new DatabaseException(e);
Madan Jampani12390c12014-11-12 00:35:56 -080060 }
61 }
62
63 @Override
Madan Jampani1d3494e2014-11-20 11:24:22 -080064 public CompletableFuture<Void> lockAsync(int leaseDurationMillis) {
Madan Jampani1769a1a2014-11-19 21:51:44 -080065 if (isLocked() || tryLock(leaseDurationMillis)) {
66 return CompletableFuture.<Void>completedFuture(null);
67 }
68 return lockManager.lockIfAvailable(this, leaseDurationMillis);
69 }
70
71 @Override
Madan Jampani12390c12014-11-12 00:35:56 -080072 public boolean tryLock(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -080073 if (databaseService.putIfAbsent(
Madan Jampanic22123d2014-11-12 02:12:19 -080074 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
75 path,
Madan Jampania88d1f52014-11-14 16:45:24 -080076 lockId)) {
77 isLocked.set(true);
78 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
79 return true;
80 }
81 return false;
Madan Jampani12390c12014-11-12 00:35:56 -080082 }
83
84 @Override
85 public boolean tryLock(
Madan Jampani1769a1a2014-11-19 21:51:44 -080086 int waitTimeMillis,
Madan Jampani71582ed2014-11-18 10:06:01 -080087 int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -080088 if (isLocked() || tryLock(leaseDurationMillis)) {
Madan Jampania88d1f52014-11-14 16:45:24 -080089 return true;
Madan Jampani12390c12014-11-12 00:35:56 -080090 }
Madan Jampani1769a1a2014-11-19 21:51:44 -080091
92 CompletableFuture<Void> future =
Madan Jampania88d1f52014-11-14 16:45:24 -080093 lockManager.lockIfAvailable(this, waitTimeMillis, leaseDurationMillis);
94 try {
Madan Jampani1769a1a2014-11-19 21:51:44 -080095 future.get(waitTimeMillis, TimeUnit.MILLISECONDS);
Madan Jampania88d1f52014-11-14 16:45:24 -080096 return true;
Madan Jampani71582ed2014-11-18 10:06:01 -080097 } catch (ExecutionException e) {
98 throw new DatabaseException(e);
Madan Jampania88d1f52014-11-14 16:45:24 -080099 } catch (TimeoutException e) {
100 log.debug("Timed out waiting to acquire lock for {}", path);
101 return false;
102 }
Madan Jampani12390c12014-11-12 00:35:56 -0800103 }
104
105 @Override
106 public boolean isLocked() {
107 if (isLocked.get()) {
108 // We rely on local information to check
Madan Jampania88d1f52014-11-14 16:45:24 -0800109 // if the lock expired.
Madan Jampani12390c12014-11-12 00:35:56 -0800110 // This should should make this call
Madan Jampania88d1f52014-11-14 16:45:24 -0800111 // light weight, while still retaining the
Madan Jampani12390c12014-11-12 00:35:56 -0800112 // safety guarantees.
113 if (DateTime.now().isAfter(lockExpirationTime)) {
114 isLocked.set(false);
115 return false;
Madan Jampanif5d263b2014-11-13 10:04:40 -0800116 } else {
117 return true;
Madan Jampani12390c12014-11-12 00:35:56 -0800118 }
119 }
Madan Jampanif5d263b2014-11-13 10:04:40 -0800120 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800121 }
122
123 @Override
124 public void unlock() {
125 if (!isLocked()) {
126 return;
127 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800128 if (databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId)) {
129 isLocked.set(false);
130 }
Madan Jampani12390c12014-11-12 00:35:56 -0800131 }
132 }
133
134 @Override
135 public boolean extendExpiration(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -0800136 if (!isLocked()) {
137 log.warn("Ignoring request to extend expiration for lock {}."
138 + " ExtendExpiration must be called for locks that are already acquired.", path);
Pavlin Radoslavov20ded692014-11-17 16:03:15 -0800139 return false;
Madan Jampania88d1f52014-11-14 16:45:24 -0800140 }
141
142 if (databaseService.putIfValueMatches(
143 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
144 path,
145 lockId,
146 lockId)) {
147 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
148 log.debug("Succeeded in extending lock {} expiration time to {}", lockExpirationTime);
Madan Jampani12390c12014-11-12 00:35:56 -0800149 return true;
150 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800151 log.info("Failed to extend expiration for {}", path);
152 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800153 }
154 }
Madan Jampania88d1f52014-11-14 16:45:24 -0800155}