Author: mreutegg
Date: Tue Jan 26 07:15:28 2016
New Revision: 1726741
URL: http://svn.apache.org/viewvc?rev=1726741&view=rev
Log:
OAK-3833: Allow to acquire multiple locks
Apply patch by Tomek Rekawek
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocks.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java?rev=1726741&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java
Tue Jan 26 07:15:28 2016
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document.locks;
+
+import static com.google.common.collect.Lists.reverse;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * This class exposes a list of locks as a single Lock instance.
+ */
+class BulkLock implements Lock {
+
+ private final List<Lock> locks;
+
+ public BulkLock(List<Lock> locks) {
+ this.locks = locks;
+ }
+
+ @Override
+ public void lock() {
+ for (Lock l : locks) {
+ l.lock();
+ }
+ }
+
+ public void unlock() {
+ for (Lock l : reverse(locks)) {
+ l.unlock();
+ }
+ }
+
+ @Override
+ public void lockInterruptibly() throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean tryLock() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean tryLock(long time, TimeUnit unit) throws
InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Condition newCondition() {
+ throw new UnsupportedOperationException();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/BulkLock.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocks.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocks.java?rev=1726741&r1=1726740&r2=1726741&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocks.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocks.java
Tue Jan 26 07:15:28 2016
@@ -16,16 +16,25 @@
*/
package org.apache.jackrabbit.oak.plugins.document.locks;
+import java.util.Collection;
import java.util.concurrent.locks.Lock;
public interface NodeDocumentLocks {
/**
- * Acquires a log for the given key.
+ * Acquires a lock for the given key.
*
* @param key a key.
* @return the acquired lock for the given key.
*/
Lock acquire(String key);
+ /**
+ * Acquires locks for the given keys. Locks are sorted before the operation
+ * to avoid deadlocks.
+ *
+ * @param keys keys
+ * @return the object wrapping the acquired locks
+ */
+ Lock acquire(Collection<String> keys);
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java?rev=1726741&r1=1726740&r2=1726741&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/StripedNodeDocumentLocks.java
Tue Jan 26 07:15:28 2016
@@ -16,21 +16,46 @@
*/
package org.apache.jackrabbit.oak.plugins.document.locks;
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.filter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.locks.Lock;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+
import com.google.common.util.concurrent.Striped;
public class StripedNodeDocumentLocks implements NodeDocumentLocks {
+ private static final String ROOT = Utils.getIdFromPath("/");
+
/**
* Locks to ensure cache consistency on reads, writes and invalidation.
*/
private final Striped<Lock> locks = Striped.lock(4096);
- private final Lock rootLock = Striped.lock(1).get("0:/");
+ private final Lock rootLock = Striped.lock(1).get(ROOT);
@Override
public Lock acquire(String key) {
- Lock lock = "0:/".equals(key) ? rootLock : locks.get(key);
+ Lock lock = ROOT.equals(key) ? rootLock : locks.get(key);
+ lock.lock();
+ return lock;
+ }
+
+ @Override
+ public Lock acquire(Collection<String> keys) {
+ List<Lock> lockList = new ArrayList<Lock>();
+ if (keys.contains(ROOT)) {
+ lockList.add(rootLock);
+ }
+ for (Lock l : locks.bulkGet(filter(keys, not(equalTo(ROOT))))) {
+ lockList.add(l);
+ }
+ Lock lock = new BulkLock(lockList);
lock.lock();
return lock;
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java?rev=1726741&r1=1726740&r2=1726741&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/locks/TreeNodeDocumentLocks.java
Tue Jan 26 07:15:28 2016
@@ -18,6 +18,10 @@ package org.apache.jackrabbit.oak.plugin
import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
@@ -28,6 +32,8 @@ import javax.annotation.Nonnull;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Striped;
public class TreeNodeDocumentLocks implements NodeDocumentLocks {
@@ -53,7 +59,7 @@ public class TreeNodeDocumentLocks imple
private volatile AtomicLong lockAcquisitionCounter;
/**
- * Acquires a log for the given key. The returned tree lock will also hold
+ * Acquires a lock for the given key. The returned tree lock will also hold
* a shared lock on the parent key.
*
* @param key a key.
@@ -68,6 +74,42 @@ public class TreeNodeDocumentLocks imple
lock.lock();
return lock;
}
+
+ /**
+ * This implementation creates two sequences of locks (for the keys and for
+ * the their parents) using {@link #locks} and {@link #parentLocks}. Then
+ * both sequences are zipped into pairs (parentLock, lock) and passed to
the
+ * {@link TreeLock#shared(ReadWriteLock, Lock)}. After that all tree locks
+ * are acquired.
+ * <p>
+ * Since we only acquire a parentLock.read, there's no danger of
+ * deadlock caused by interleaving locks from two different stripes by two
+ * threads. The only place where the parentLock.write is acquired is the
+ * {@link #acquireExclusive(String)} and that method doesn't acquire locks
in bulk.
+ */
+ @Override
+ public Lock acquire(Collection<String> keys) {
+ if (lockAcquisitionCounter != null) {
+ lockAcquisitionCounter.addAndGet(keys.size());
+ }
+
+ Iterable<String> parentKeys = Iterables.transform(keys, new
Function<String, String>() {
+ @Override
+ public String apply(String keys) {
+ return getParentId(keys);
+ }
+ });
+ Iterator<Lock> lockIt = locks.bulkGet(keys).iterator();
+ Iterator<ReadWriteLock> parentLockIt =
parentLocks.bulkGet(parentKeys).iterator();
+
+ List<Lock> acquired = new ArrayList<Lock>(keys.size());
+ while (lockIt.hasNext()) {
+ acquired.add(TreeLock.shared(parentLockIt.next(), lockIt.next()));
+ }
+ Lock lock = new BulkLock(acquired);
+ lock.lock();
+ return lock;
+ }
/**
* Acquires an exclusive lock on the given parent key. Use this method to
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java?rev=1726741&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
Tue Jan 26 07:15:28 2016
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document.locks;
+
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NodeDocumentLocksTest {
+
+ @Parameters(name = "{1}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { { new
StripedNodeDocumentLocks(), "StripedNodeDocumentLocks" },
+ { new TreeNodeDocumentLocks(), "TreeNodeDocumentLocks" } });
+ }
+
+ private final NodeDocumentLocks locks;
+
+ public NodeDocumentLocksTest(NodeDocumentLocks locks, String name) {
+ this.locks = locks;
+ }
+
+ @Test
+ public void testBulkAcquireNonConflicting() throws InterruptedException {
+ testBulkAcquire(false);
+ }
+
+ @Test
+ public void testBulkAcquireConflicting() throws InterruptedException {
+ testBulkAcquire(true);
+ }
+
+ private void testBulkAcquire(boolean conflicting) throws
InterruptedException {
+ int threadCount = 10;
+ int locksPerThread = 100;
+
+ List<Thread> threads = new ArrayList<Thread>();
+ String keyName = conflicting ? "lock_%d" : "lock_%d_%d";
+ for (int i = 0; i < threadCount; i++) {
+ final List<String> keys = new ArrayList<String>(locksPerThread);
+ for (int j = 0; j < locksPerThread; j++) {
+ keys.add(String.format(keyName, j, i));
+ }
+ threads.add(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Lock lock = locks.acquire(keys);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ } finally {
+ lock.unlock();
+ }
+ }
+ }));
+ }
+
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ t.join(10000);
+ if (t.isAlive()) {
+ fail("Thread hasn't stopped in 10s");
+ }
+ }
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/locks/NodeDocumentLocksTest.java
------------------------------------------------------------------------------
svn:eol-style = native