blob: f84baafdcf03fd24fba9e56a9c798c3ac8c52a4a [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 Jampani12390c12014-11-12 00:35:56 -080056 if (isLocked() && lockExpirationTime.isAfter(DateTime.now().plusMillis(leaseDurationMillis))) {
Madan Jampani12390c12014-11-12 00:35:56 -080057 return;
58 } else {
Madan Jampani71582ed2014-11-18 10:06:01 -080059 CompletableFuture<DateTime> future =
60 lockManager.lockIfAvailable(this, leaseDurationMillis);
61 try {
62 lockExpirationTime = future.get();
63 } catch (ExecutionException e) {
64 throw new DatabaseException(e);
65 }
Madan Jampani12390c12014-11-12 00:35:56 -080066 }
67 }
68
69 @Override
70 public boolean tryLock(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -080071 if (databaseService.putIfAbsent(
Madan Jampanic22123d2014-11-12 02:12:19 -080072 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
73 path,
Madan Jampania88d1f52014-11-14 16:45:24 -080074 lockId)) {
75 isLocked.set(true);
76 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
77 return true;
78 }
79 return false;
Madan Jampani12390c12014-11-12 00:35:56 -080080 }
81
82 @Override
83 public boolean tryLock(
84 long waitTimeMillis,
Madan Jampani71582ed2014-11-18 10:06:01 -080085 int leaseDurationMillis) throws InterruptedException {
Madan Jampania88d1f52014-11-14 16:45:24 -080086 if (tryLock(leaseDurationMillis)) {
87 return true;
Madan Jampani12390c12014-11-12 00:35:56 -080088 }
Madan Jampania88d1f52014-11-14 16:45:24 -080089 CompletableFuture<DateTime> future =
90 lockManager.lockIfAvailable(this, waitTimeMillis, leaseDurationMillis);
91 try {
92 lockExpirationTime = future.get(waitTimeMillis, TimeUnit.MILLISECONDS);
93 return true;
Madan Jampani71582ed2014-11-18 10:06:01 -080094 } catch (ExecutionException e) {
95 throw new DatabaseException(e);
Madan Jampania88d1f52014-11-14 16:45:24 -080096 } catch (TimeoutException e) {
97 log.debug("Timed out waiting to acquire lock for {}", path);
98 return false;
99 }
Madan Jampani12390c12014-11-12 00:35:56 -0800100 }
101
102 @Override
103 public boolean isLocked() {
104 if (isLocked.get()) {
105 // We rely on local information to check
Madan Jampania88d1f52014-11-14 16:45:24 -0800106 // if the lock expired.
Madan Jampani12390c12014-11-12 00:35:56 -0800107 // This should should make this call
Madan Jampania88d1f52014-11-14 16:45:24 -0800108 // light weight, while still retaining the
Madan Jampani12390c12014-11-12 00:35:56 -0800109 // safety guarantees.
110 if (DateTime.now().isAfter(lockExpirationTime)) {
111 isLocked.set(false);
112 return false;
Madan Jampanif5d263b2014-11-13 10:04:40 -0800113 } else {
114 return true;
Madan Jampani12390c12014-11-12 00:35:56 -0800115 }
116 }
Madan Jampanif5d263b2014-11-13 10:04:40 -0800117 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800118 }
119
120 @Override
121 public void unlock() {
122 if (!isLocked()) {
123 return;
124 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800125 if (databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId)) {
126 isLocked.set(false);
127 }
Madan Jampani12390c12014-11-12 00:35:56 -0800128 }
129 }
130
131 @Override
132 public boolean extendExpiration(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -0800133 if (!isLocked()) {
134 log.warn("Ignoring request to extend expiration for lock {}."
135 + " ExtendExpiration must be called for locks that are already acquired.", path);
Pavlin Radoslavov20ded692014-11-17 16:03:15 -0800136 return false;
Madan Jampania88d1f52014-11-14 16:45:24 -0800137 }
138
139 if (databaseService.putIfValueMatches(
140 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
141 path,
142 lockId,
143 lockId)) {
144 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
145 log.debug("Succeeded in extending lock {} expiration time to {}", lockExpirationTime);
Madan Jampani12390c12014-11-12 00:35:56 -0800146 return true;
147 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800148 log.info("Failed to extend expiration for {}", path);
149 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800150 }
151 }
Madan Jampania88d1f52014-11-14 16:45:24 -0800152}