blob: 5717d7423f5cd74cf16d6d0e62ae4b0255a37d20 [file] [log] [blame]
alshabibab984662014-12-04 18:56:18 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.service.impl;
Madan Jampani12390c12014-11-12 00:35:56 -080017
Madan Jampani1ee91782014-11-20 20:24:24 -080018import static com.google.common.base.Verify.verify;
Madan Jampania88d1f52014-11-14 16:45:24 -080019import static org.slf4j.LoggerFactory.getLogger;
20
Ray Milkey241b96a2014-11-17 13:08:20 -080021import java.nio.charset.StandardCharsets;
Madan Jampani1ee91782014-11-20 20:24:24 -080022import java.util.Arrays;
Madan Jampani12390c12014-11-12 00:35:56 -080023import java.util.UUID;
24import java.util.concurrent.CompletableFuture;
25import java.util.concurrent.ExecutionException;
26import java.util.concurrent.TimeUnit;
27import java.util.concurrent.TimeoutException;
28import java.util.concurrent.atomic.AtomicBoolean;
29
30import org.joda.time.DateTime;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.cluster.ClusterService;
32import org.onosproject.store.service.DatabaseException;
33import org.onosproject.store.service.DatabaseService;
34import org.onosproject.store.service.Lock;
35import org.onosproject.store.service.VersionedValue;
Madan Jampania88d1f52014-11-14 16:45:24 -080036import org.slf4j.Logger;
Madan Jampani12390c12014-11-12 00:35:56 -080037
38/**
39 * A distributed lock implementation.
40 */
41public class DistributedLock implements Lock {
42
Madan Jampania88d1f52014-11-14 16:45:24 -080043 private final Logger log = getLogger(getClass());
44
Madan Jampani12390c12014-11-12 00:35:56 -080045 private final DistributedLockManager lockManager;
46 private final DatabaseService databaseService;
47 private final String path;
48 private DateTime lockExpirationTime;
49 private AtomicBoolean isLocked = new AtomicBoolean(false);
Madan Jampani1ee91782014-11-20 20:24:24 -080050 private volatile long epoch = 0;
Madan Jampani12390c12014-11-12 00:35:56 -080051 private byte[] lockId;
52
53 public DistributedLock(
54 String path,
55 DatabaseService databaseService,
56 ClusterService clusterService,
57 DistributedLockManager lockManager) {
58
59 this.path = path;
60 this.databaseService = databaseService;
61 this.lockManager = lockManager;
62 this.lockId =
Ray Milkey241b96a2014-11-17 13:08:20 -080063 (UUID.randomUUID().toString() + "::" +
64 clusterService.getLocalNode().id().toString()).
65 getBytes(StandardCharsets.UTF_8);
Madan Jampani12390c12014-11-12 00:35:56 -080066 }
67
68 @Override
69 public String path() {
70 return path;
71 }
72
73 @Override
Madan Jampani71582ed2014-11-18 10:06:01 -080074 public void lock(int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -080075 try {
76 lockAsync(leaseDurationMillis).get();
77 } catch (ExecutionException e) {
78 throw new DatabaseException(e);
Madan Jampani12390c12014-11-12 00:35:56 -080079 }
80 }
81
82 @Override
Madan Jampani1d3494e2014-11-20 11:24:22 -080083 public CompletableFuture<Void> lockAsync(int leaseDurationMillis) {
Madan Jampaniddaffd02014-11-21 13:12:09 -080084 try {
85 if (isLocked() || tryLock(leaseDurationMillis)) {
86 return CompletableFuture.<Void>completedFuture(null);
87 }
88 return lockManager.lockIfAvailable(this, leaseDurationMillis);
89 } catch (DatabaseException e) {
90 CompletableFuture<Void> lockFuture = new CompletableFuture<>();
91 lockFuture.completeExceptionally(e);
92 return lockFuture;
Madan Jampani1769a1a2014-11-19 21:51:44 -080093 }
Madan Jampani1769a1a2014-11-19 21:51:44 -080094 }
95
96 @Override
Madan Jampani12390c12014-11-12 00:35:56 -080097 public boolean tryLock(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -080098 if (databaseService.putIfAbsent(
Madan Jampanic22123d2014-11-12 02:12:19 -080099 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
100 path,
Madan Jampania88d1f52014-11-14 16:45:24 -0800101 lockId)) {
Madan Jampani1ee91782014-11-20 20:24:24 -0800102 VersionedValue vv =
103 databaseService.get(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path);
104 verify(Arrays.equals(vv.value(), lockId));
105 epoch = vv.version();
Madan Jampania88d1f52014-11-14 16:45:24 -0800106 isLocked.set(true);
107 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
108 return true;
109 }
110 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800111 }
112
113 @Override
114 public boolean tryLock(
Madan Jampani1769a1a2014-11-19 21:51:44 -0800115 int waitTimeMillis,
Madan Jampani71582ed2014-11-18 10:06:01 -0800116 int leaseDurationMillis) throws InterruptedException {
Madan Jampani1769a1a2014-11-19 21:51:44 -0800117 if (isLocked() || tryLock(leaseDurationMillis)) {
Madan Jampania88d1f52014-11-14 16:45:24 -0800118 return true;
Madan Jampani12390c12014-11-12 00:35:56 -0800119 }
Madan Jampani1769a1a2014-11-19 21:51:44 -0800120
121 CompletableFuture<Void> future =
Madan Jampania88d1f52014-11-14 16:45:24 -0800122 lockManager.lockIfAvailable(this, waitTimeMillis, leaseDurationMillis);
123 try {
Madan Jampani1769a1a2014-11-19 21:51:44 -0800124 future.get(waitTimeMillis, TimeUnit.MILLISECONDS);
Madan Jampania88d1f52014-11-14 16:45:24 -0800125 return true;
Madan Jampani71582ed2014-11-18 10:06:01 -0800126 } catch (ExecutionException e) {
127 throw new DatabaseException(e);
Madan Jampania88d1f52014-11-14 16:45:24 -0800128 } catch (TimeoutException e) {
129 log.debug("Timed out waiting to acquire lock for {}", path);
130 return false;
131 }
Madan Jampani12390c12014-11-12 00:35:56 -0800132 }
133
134 @Override
135 public boolean isLocked() {
136 if (isLocked.get()) {
137 // We rely on local information to check
Madan Jampania88d1f52014-11-14 16:45:24 -0800138 // if the lock expired.
Madan Jampani12390c12014-11-12 00:35:56 -0800139 // This should should make this call
Madan Jampania88d1f52014-11-14 16:45:24 -0800140 // light weight, while still retaining the
Madan Jampani12390c12014-11-12 00:35:56 -0800141 // safety guarantees.
142 if (DateTime.now().isAfter(lockExpirationTime)) {
143 isLocked.set(false);
144 return false;
Madan Jampanif5d263b2014-11-13 10:04:40 -0800145 } else {
146 return true;
Madan Jampani12390c12014-11-12 00:35:56 -0800147 }
148 }
Madan Jampanif5d263b2014-11-13 10:04:40 -0800149 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800150 }
151
152 @Override
Madan Jampani1ee91782014-11-20 20:24:24 -0800153 public long epoch() {
154 return epoch;
155 }
156
157 @Override
Madan Jampani12390c12014-11-12 00:35:56 -0800158 public void unlock() {
159 if (!isLocked()) {
160 return;
161 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800162 if (databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId)) {
163 isLocked.set(false);
164 }
Madan Jampani12390c12014-11-12 00:35:56 -0800165 }
166 }
167
168 @Override
169 public boolean extendExpiration(int leaseDurationMillis) {
Madan Jampania88d1f52014-11-14 16:45:24 -0800170 if (!isLocked()) {
171 log.warn("Ignoring request to extend expiration for lock {}."
172 + " ExtendExpiration must be called for locks that are already acquired.", path);
Pavlin Radoslavov20ded692014-11-17 16:03:15 -0800173 return false;
Madan Jampania88d1f52014-11-14 16:45:24 -0800174 }
175
176 if (databaseService.putIfValueMatches(
177 DistributedLockManager.ONOS_LOCK_TABLE_NAME,
178 path,
179 lockId,
180 lockId)) {
181 lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis);
182 log.debug("Succeeded in extending lock {} expiration time to {}", lockExpirationTime);
Madan Jampani12390c12014-11-12 00:35:56 -0800183 return true;
184 } else {
Madan Jampania88d1f52014-11-14 16:45:24 -0800185 log.info("Failed to extend expiration for {}", path);
186 return false;
Madan Jampani12390c12014-11-12 00:35:56 -0800187 }
188 }
Madan Jampania88d1f52014-11-14 16:45:24 -0800189}