blob: 7575ed903c06c3da8f811c125ca400d7d22036ba [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;
Madan Jampani1769a1a2014-11-19 21:51:44 -08009import java.util.concurrent.Future;
Madan Jampani12390c12014-11-12 00:35:56 -080010import java.util.concurrent.TimeUnit;
11import java.util.concurrent.TimeoutException;
12import java.util.concurrent.atomic.AtomicBoolean;
13
14import org.joda.time.DateTime;
15import org.onlab.onos.cluster.ClusterService;
Madan Jampani71582ed2014-11-18 10:06:01 -080016import org.onlab.onos.store.service.DatabaseException;
Madan Jampani12390c12014-11-12 00:35:56 -080017import org.onlab.onos.store.service.DatabaseService;
18import org.onlab.onos.store.service.Lock;
Madan Jampania88d1f52014-11-14 16:45:24 -080019import org.slf4j.Logger;
Madan Jampani12390c12014-11-12 00:35:56 -080020
21/**
22 * A distributed lock implementation.
23 */
24public class DistributedLock implements Lock {
25
Madan Jampania88d1f52014-11-14 16:45:24 -080026 private final Logger log = getLogger(getClass());
27
Madan Jampani12390c12014-11-12 00:35:56 -080028 private final DistributedLockManager lockManager;
29 private final DatabaseService databaseService;
30 private final String path;
31 private DateTime lockExpirationTime;
32 private AtomicBoolean isLocked = new AtomicBoolean(false);
33 private byte[] lockId;
34
35 public DistributedLock(
36 String path,
37 DatabaseService databaseService,
38 ClusterService clusterService,
39 DistributedLockManager lockManager) {
40
41 this.path = path;
42 this.databaseService = databaseService;
43 this.lockManager = lockManager;
44 this.lockId =
Ray Milkey241b96a2014-11-17 13:08:20 -080045 (UUID.randomUUID().toString() + "::" +
46 clusterService.getLocalNode().id().toString()).
47 getBytes(StandardCharsets.UTF_8);
Madan Jampani12390c12014-11-12 00:35:56 -080048 }
49
50 @Override
51 public String path() {
52 return path;
53 }
54
55 @Override
Madan Jampani71582ed2014-11-18 10:06:01 -080056 public void lock(int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -080057 try {
58 lockAsync(leaseDurationMillis).get();
59 } catch (ExecutionException e) {
60 throw new DatabaseException(e);
Madan Jampani12390c12014-11-12 00:35:56 -080061 }
62 }
63
64 @Override
Madan Jampani1769a1a2014-11-19 21:51:44 -080065 public Future<Void> lockAsync(int leaseDurationMillis) {
66 if (isLocked() || tryLock(leaseDurationMillis)) {
67 return CompletableFuture.<Void>completedFuture(null);
68 }
69 return lockManager.lockIfAvailable(this, leaseDurationMillis);
70 }
71
72 @Override
Madan Jampani12390c12014-11-12 00:35:56 -080073 public boolean tryLock(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -080074 if (databaseService.putIfAbsent(
Madan Jampanic22123d2014-11-12 02:12:19 -080075 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
76 path,
Madan Jampania88d1f52014-11-14 16:45:24 -080077 lockId)) {
78 isLocked.set(true);
79 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
80 return true;
81 }
82 return false;
Madan Jampani12390c12014-11-12 00:35:56 -080083 }
84
85 @Override
86 public boolean tryLock(
Madan Jampani1769a1a2014-11-19 21:51:44 -080087 int waitTimeMillis,
Madan Jampani71582ed2014-11-18 10:06:01 -080088 int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -080089 if (isLocked() || tryLock(leaseDurationMillis)) {
Madan Jampania88d1f52014-11-14 16:45:24 -080090 return true;
Madan Jampani12390c12014-11-12 00:35:56 -080091 }
Madan Jampani1769a1a2014-11-19 21:51:44 -080092
93 CompletableFuture<Void> future =
Madan Jampania88d1f52014-11-14 16:45:24 -080094 lockManager.lockIfAvailable(this, waitTimeMillis, leaseDurationMillis);
95 try {
Madan Jampani1769a1a2014-11-19 21:51:44 -080096 future.get(waitTimeMillis, TimeUnit.MILLISECONDS);
Madan Jampania88d1f52014-11-14 16:45:24 -080097 return true;
Madan Jampani71582ed2014-11-18 10:06:01 -080098 } catch (ExecutionException e) {
99 throw new DatabaseException(e);
Madan Jampania88d1f52014-11-14 16:45:24 -0800100 } catch (TimeoutException e) {
101 log.debug("Timed out waiting to acquire lock for {}", path);
102 return false;
103 }
Madan Jampani12390c12014-11-12 00:35:56 -0800104 }
105
106 @Override
107 public boolean isLocked() {
108 if (isLocked.get()) {
109 // We rely on local information to check
Madan Jampania88d1f52014-11-14 16:45:24 -0800110 // if the lock expired.
Madan Jampani12390c12014-11-12 00:35:56 -0800111 // This should should make this call
Madan Jampania88d1f52014-11-14 16:45:24 -0800112 // light weight, while still retaining the
Madan Jampani12390c12014-11-12 00:35:56 -0800113 // safety guarantees.
114 if (DateTime.now().isAfter(lockExpirationTime)) {
115 isLocked.set(false);
116 return false;
Madan Jampanif5d263b2014-11-13 10:04:40 -0800117 } else {
118 return true;
Madan Jampani12390c12014-11-12 00:35:56 -0800119 }
120 }
Madan Jampanif5d263b2014-11-13 10:04:40 -0800121 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800122 }
123
124 @Override
125 public void unlock() {
126 if (!isLocked()) {
127 return;
128 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800129 if (databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId)) {
130 isLocked.set(false);
131 }
Madan Jampani12390c12014-11-12 00:35:56 -0800132 }
133 }
134
135 @Override
136 public boolean extendExpiration(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -0800137 if (!isLocked()) {
138 log.warn("Ignoring request to extend expiration for lock {}."
139 + " ExtendExpiration must be called for locks that are already acquired.", path);
Pavlin Radoslavov20ded692014-11-17 16:03:15 -0800140 return false;
Madan Jampania88d1f52014-11-14 16:45:24 -0800141 }
142
143 if (databaseService.putIfValueMatches(
144 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
145 path,
146 lockId,
147 lockId)) {
148 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
149 log.debug("Succeeded in extending lock {} expiration time to {}", lockExpirationTime);
Madan Jampani12390c12014-11-12 00:35:56 -0800150 return true;
151 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800152 log.info("Failed to extend expiration for {}", path);
153 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800154 }
155 }
Madan Jampania88d1f52014-11-14 16:45:24 -0800156}