This is an automated email from the ASF dual-hosted git repository.
adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 43caa56e18 HDDS-7178. [Multi-Tenant] Use optimistic read in Ranger
background sync (#3725)
43caa56e18 is described below
commit 43caa56e18c4654cd3b58452152b14f3056c0376
Author: Ethan Rose <[email protected]>
AuthorDate: Fri Feb 17 08:51:27 2023 -0800
HDDS-7178. [Multi-Tenant] Use optimistic read in Ranger background sync
(#3725)
---
.../ozone/om/multitenant/AuthorizerLock.java | 15 +++++--
.../ozone/om/multitenant/AuthorizerLockImpl.java | 24 +++++++++--
.../ozone/om/service/OMRangerBGSyncService.java | 46 ++++++++++++++--------
.../hadoop/ozone/om/TestAuthorizerLockImpl.java | 37 ++++++++++++++++-
4 files changed, 97 insertions(+), 25 deletions(-)
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLock.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLock.java
index 448e9d789a..1dcb0f0cd6 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLock.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLock.java
@@ -42,10 +42,19 @@ public interface AuthorizerLock {
void unlockRead(long stamp);
/**
- * A wrapper around tryReadLock() that throws when timed out.
- * @return stamp
+ * @return stamp that can be passed to
+ * {@link this#validateOptimisticRead(long)} to check if a write lock was
+ * acquired since the stamp was issued.
+ * @throws IOException If an ongoing write prevents the lock from moving to
+ * the read state for longer than the timeout.
+ */
+ long tryOptimisticReadThrowOnTimeout() throws IOException;
+
+ /**
+ * @return True if the write lock was not acquired since this stamp was
+ * issued for an optimistic read. False otherwise.
*/
- long tryReadLockThrowOnTimeout() throws IOException;
+ boolean validateOptimisticRead(long stamp);
/**
* Attempt to acquire the write lock to authorizer with a timeout.
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLockImpl.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLockImpl.java
index f3ed2c3130..bffda3ed8f 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLockImpl.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/AuthorizerLockImpl.java
@@ -68,7 +68,7 @@ public class AuthorizerLockImpl implements AuthorizerLock {
}
@Override
- public long tryReadLockThrowOnTimeout() throws IOException {
+ public long tryOptimisticReadThrowOnTimeout() throws IOException {
long stamp;
try {
@@ -80,11 +80,27 @@ public class AuthorizerLockImpl implements AuthorizerLock {
throw new OMException("Timed out acquiring authorizer read lock."
+ " Another multi-tenancy request is in-progress. Try again later",
ResultCodes.TIMEOUT);
+ }
+
+ long optimisticStamp = authorizerStampedLock.tryConvertToOptimisticRead(
+ stamp);
+ if (optimisticStamp == 0L) {
+ // This should never happen. If we reached this point we are holding a
+ // read lock and providing its stamp so the lock should be in read mode
+ // already. This would return a valid stamp, not 0.
+ unlockRead(stamp);
+ throw new OMException("Failed to convert read lock to optimistic read.",
+ INTERNAL_ERROR);
} else if (LOG.isDebugEnabled()) {
- LOG.debug("Acquired authorizer read lock from thread {} with stamp {}",
- Thread.currentThread().getId(), stamp);
+ LOG.debug("Acquired authorizer optimistic read from thread {} with" +
+ " stamp {}", Thread.currentThread().getId(), optimisticStamp);
}
- return stamp;
+ return optimisticStamp;
+ }
+
+ @Override
+ public boolean validateOptimisticRead(long stamp) {
+ return authorizerStampedLock.validate(stamp);
}
@Override
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/OMRangerBGSyncService.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/OMRangerBGSyncService.java
index 82e0d4972a..eb6f852217 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/OMRangerBGSyncService.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/OMRangerBGSyncService.java
@@ -296,6 +296,7 @@ public class OMRangerBGSyncService extends
BackgroundService {
* @return true if completed successfully, false if any exception is thrown.
*/
public synchronized boolean triggerRangerSyncOnce() {
+ int attempt = 0;
try {
long dbOzoneServiceVersion = getOMDBRangerServiceVersion();
long rangerOzoneServiceVersion = getRangerOzoneServicePolicyVersion();
@@ -319,14 +320,11 @@ public class OMRangerBGSyncService extends
BackgroundService {
// A maximum of MAX_ATTEMPT times will be attempted each time the sync
// service is run. MAX_ATTEMPT should at least be 2 to make sure OM DB
// has the up-to-date Ranger service version most of the times.
- int attempt = 0;
while (dbOzoneServiceVersion != rangerOzoneServiceVersion) {
if (++attempt > MAX_ATTEMPT) {
- if (LOG.isDebugEnabled()) {
- LOG.warn("Reached maximum number of attempts ({}). Abort",
- MAX_ATTEMPT);
- }
+ LOG.warn("Reached maximum number of attempts ({}). Abort",
+ MAX_ATTEMPT);
break;
}
@@ -350,9 +348,12 @@ public class OMRangerBGSyncService extends
BackgroundService {
}
} catch (IOException | ServiceException e) {
LOG.warn("Exception during Ranger Sync", e);
- // TODO: Check for specific exception once switched to
- // RangerRestMultiTenantAccessController
return false;
+ } finally {
+ if (attempt > 0) {
+ LOG.info("Finished executing Multi-Tenancy Ranger Sync run # {} after"
+
+ "{} attempts.", runCount.get(), attempt);
+ }
}
return true;
@@ -424,7 +425,7 @@ public class OMRangerBGSyncService extends
BackgroundService {
clearPolicyAndRoleMaps();
- withReadLock(() -> {
+ withOptimisticRead(() -> {
try {
loadAllPoliciesAndRoleNamesFromRanger(baseVersion);
loadAllRolesFromRanger();
@@ -561,16 +562,29 @@ public class OMRangerBGSyncService extends
BackgroundService {
}
/**
- * Helper function to run the block with read lock held.
+ * Helper function to retry the block until it completes without a write lock
+ * being acquired during its execution. The block will be retried
+ * {@link this#MAX_ATTEMPT} times.
*/
- private void withReadLock(Runnable block) throws IOException {
- // Acquire authorizer (Ranger) read lock
- long stamp = authorizerLock.tryReadLockThrowOnTimeout();
- try {
+ private void withOptimisticRead(Runnable block) throws IOException {
+ // Acquire a stamp that will be used to check if a write occurred while we
+ // were reading.
+ // If a tenant modification is made while we are reading,
+ // retry the read operation with a new stamp until we are able to read the
+ // state without a write operation interrupting.
+ int attempt = 0;
+ boolean readSuccess = false;
+ while (!readSuccess && attempt < MAX_ATTEMPT) {
+ long stamp = authorizerLock.tryOptimisticReadThrowOnTimeout();
block.run();
- } finally {
- // Release authorizer (Ranger) read lock
- authorizerLock.unlockRead(stamp);
+ readSuccess = authorizerLock.validateOptimisticRead(stamp);
+ attempt++;
+ }
+
+ if (!readSuccess) {
+ throw new IOException("Failed to read state for Ranger background sync" +
+ " without an interrupting write operation after " + attempt +
+ " attempts.");
}
}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestAuthorizerLockImpl.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestAuthorizerLockImpl.java
index 502537cb2f..4d5850e872 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestAuthorizerLockImpl.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestAuthorizerLockImpl.java
@@ -132,14 +132,15 @@ public class TestAuthorizerLockImpl {
}
@Test
- public void testIsWriteLockHeldByCurrentThread() throws IOException {
+ public void testIsWriteLockHeldByCurrentThread() throws IOException,
+ InterruptedException {
final AuthorizerLock authorizerLock = new AuthorizerLockImpl();
Assert.assertFalse(authorizerLock.isWriteLockHeldByCurrentThread());
// Read lock does not affect the check
- long readLockStamp = authorizerLock.tryReadLockThrowOnTimeout();
+ long readLockStamp = authorizerLock.tryReadLock(100);
Assert.assertFalse(authorizerLock.isWriteLockHeldByCurrentThread());
authorizerLock.unlockRead(readLockStamp);
@@ -153,4 +154,36 @@ public class TestAuthorizerLockImpl {
Assert.assertFalse(authorizerLock.isWriteLockHeldByCurrentThread());
authorizerLock.unlockWrite(writeLockStamp);
}
+
+ @Test
+ public void testOptimisticRead() throws Exception {
+ final AuthorizerLock authorizerLock = new AuthorizerLockImpl();
+
+ // With no competing operations, an optimistic read should be valid.
+ long stamp = authorizerLock.tryOptimisticReadThrowOnTimeout();
+ Assert.assertTrue(authorizerLock.validateOptimisticRead(stamp));
+
+ // With only competing reads, an optimistic read should be valid.
+ long optStamp = authorizerLock.tryOptimisticReadThrowOnTimeout();
+ long readStamp = authorizerLock.tryReadLock(100);
+ Assert.assertTrue(readStamp > 0L);
+ Assert.assertTrue(authorizerLock.validateOptimisticRead(optStamp));
+ authorizerLock.unlockRead(readStamp);
+ Assert.assertTrue(authorizerLock.validateOptimisticRead(optStamp));
+
+ // When a write lock is held, optimistic read should time out trying to get
+ // a stamp.
+ long writeStamp = authorizerLock.tryWriteLockThrowOnTimeout();
+ Assert.assertThrows(IOException.class,
+ authorizerLock::tryOptimisticReadThrowOnTimeout);
+ authorizerLock.unlockWrite(writeStamp);
+
+ // When a write lock is acquired after the optimistic read stamp, the read
+ // stamp should be invalidated.
+ optStamp = authorizerLock.tryOptimisticReadThrowOnTimeout();
+ writeStamp = authorizerLock.tryWriteLockThrowOnTimeout();
+ Assert.assertTrue(writeStamp > 0L);
+ Assert.assertFalse(authorizerLock.validateOptimisticRead(optStamp));
+ authorizerLock.unlockWrite(writeStamp);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]