-
Notifications
You must be signed in to change notification settings - Fork 537
HDDS-12356. granular locking framework for obs #8217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,9 +21,9 @@ | |
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.ListIterator; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.Stack; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
import java.util.concurrent.locks.Lock; | ||
|
@@ -53,16 +53,16 @@ public OmRequestGatekeeper() { | |
|
||
public OmLockObject lock(OmLockInfo lockInfo) throws IOException { | ||
OmLockObject omLockObject = new OmLockObject(lockInfo); | ||
List<Lock> locks = omLockObject.getLocks(); | ||
long startTime = Time.monotonicNowNanos(); | ||
Optional<OmLockInfo.LockInfo> optionalVolumeLock = lockInfo.getVolumeLock(); | ||
Optional<OmLockInfo.LockInfo> optionalBucketLock = lockInfo.getBucketLock(); | ||
Optional<Set<OmLockInfo.LockInfo>> optionalKeyLocks = lockInfo.getKeyLocks(); | ||
List<Lock> locks = new ArrayList<>(); | ||
|
||
if (optionalVolumeLock.isPresent()) { | ||
OmLockInfo.LockInfo volumeLockInfo = optionalVolumeLock.get(); | ||
if (volumeLockInfo.isWriteLock()) { | ||
omLockObject.setLockStatType(OmLockStats.Type.WRITE); | ||
omLockObject.setReadStatsType(false); | ||
locks.add(volumeLocks.get(volumeLockInfo.getName()).writeLock()); | ||
} else { | ||
locks.add(volumeLocks.get(volumeLockInfo.getName()).readLock()); | ||
|
@@ -72,7 +72,7 @@ public OmLockObject lock(OmLockInfo lockInfo) throws IOException { | |
if (optionalBucketLock.isPresent()) { | ||
OmLockInfo.LockInfo bucketLockInfo = optionalBucketLock.get(); | ||
if (bucketLockInfo.isWriteLock()) { | ||
omLockObject.setLockStatType(OmLockStats.Type.WRITE); | ||
omLockObject.setReadStatsType(false); | ||
locks.add(bucketLocks.get(bucketLockInfo.getName()).writeLock()); | ||
} else { | ||
locks.add(bucketLocks.get(bucketLockInfo.getName()).readLock()); | ||
|
@@ -81,35 +81,24 @@ public OmLockObject lock(OmLockInfo lockInfo) throws IOException { | |
|
||
if (optionalKeyLocks.isPresent()) { | ||
for (ReadWriteLock keyLock: keyLocks.bulkGet(optionalKeyLocks.get())) { | ||
omLockObject.setLockStatType(OmLockStats.Type.WRITE); | ||
omLockObject.setReadStatsType(false); | ||
locks.add(keyLock.writeLock()); | ||
} | ||
} | ||
|
||
try { | ||
acquireLocks(locks); | ||
acquireLocks(locks, omLockObject.getLocks()); | ||
lockStatsBegin(omLockObject.getLockStats(), Time.monotonicNowNanos(), startTime); | ||
} catch (InterruptedException e) { | ||
locks.clear(); | ||
Thread.currentThread().interrupt(); | ||
throw new OMException("waiting for locks is interrupted", OMException.ResultCodes.INTERNAL_ERROR); | ||
throw new OMException("Waiting for locks is interrupted, " + lockInfo, OMException.ResultCodes.INTERNAL_ERROR); | ||
} catch (TimeoutException e) { | ||
locks.clear(); | ||
throw new OMException("Unable to get locks, timeout occurred", OMException.ResultCodes.TIMEOUT); | ||
throw new OMException("Timeout occurred for locks " + lockInfo, OMException.ResultCodes.TIMEOUT); | ||
} | ||
return omLockObject; | ||
} | ||
|
||
/* | ||
Optional: If we want more diagnostic info on the type of lock that failed to be acquired (volume, bucket, or key), | ||
We can make the parameter a list of objects that wrap the Lock with information about its type. | ||
Note that logging the specific volume, bucket or keys this lock was trying to acquire is not helpful and | ||
misleading because collisions within the stripe lock might mean we are blocked on a request for a completely | ||
different part of the namespace. | ||
Obtaining the thread ID that we were waiting on would be more useful, but there is no easy way to do that. | ||
*/ | ||
private void acquireLocks(List<Lock> locks) throws TimeoutException, InterruptedException { | ||
List<Lock> acquiredLocks = new ArrayList<>(locks.size()); | ||
private void acquireLocks(List<Lock> locks, Stack<Lock> acquiredLocks) throws TimeoutException, InterruptedException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Concurrently, if the If we are adding timeout support, we have to update the design doc first. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added section for having timeout in design doc for lock |
||
for (Lock lock: locks) { | ||
if (lock.tryLock(LOCK_TIMEOUT_DEFAULT, TimeUnit.MILLISECONDS)) { | ||
try { | ||
|
@@ -127,63 +116,67 @@ private void acquireLocks(List<Lock> locks) throws TimeoutException, Interrupted | |
} | ||
} | ||
|
||
public void unlock(OmLockObject lockObject) { | ||
releaseLocks(lockObject.getLocks()); | ||
lockStatsEnd(lockObject.getLockStats(), lockObject.getLockStatType()); | ||
lockObject.getLocks().clear(); | ||
} | ||
|
||
private void releaseLocks(List<Lock> locks) { | ||
ListIterator<Lock> reverseIterator = locks.listIterator(locks.size()); | ||
while (reverseIterator.hasPrevious()) { | ||
Lock lock = reverseIterator.previous(); | ||
lock.unlock(); | ||
private void releaseLocks(Stack<Lock> locks) { | ||
while (!locks.empty()) { | ||
locks.pop().unlock(); | ||
} | ||
} | ||
|
||
private static void lockStatsBegin(OmLockStats lockStats, long endTime, long startTime) { | ||
lockStats.add(endTime - startTime, OmLockStats.Type.WAIT); | ||
lockStats.addWaitLockNanos(endTime - startTime); | ||
lockStats.setLockStartTime(endTime); | ||
} | ||
|
||
private static void lockStatsEnd(OmLockStats lockStats, OmLockStats.Type type) { | ||
private static void lockStatsEnd(OmLockStats lockStats, boolean readStatsType) { | ||
if (lockStats.getLockStartTime() > 0) { | ||
lockStats.add(Time.monotonicNowNanos() - lockStats.getLockStartTime(), type); | ||
if (readStatsType) { | ||
lockStats.addReadLockNanos(Time.monotonicNowNanos() - lockStats.getLockStartTime()); | ||
} else { | ||
lockStats.addWriteLockNanos(Time.monotonicNowNanos() - lockStats.getLockStartTime()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Lock information after taking locks. | ||
* Lock information after taking locks, and to be used to release locks. | ||
*/ | ||
public static class OmLockObject { | ||
public static class OmLockObject implements AutoCloseable { | ||
private final OmLockInfo omLockInfo; | ||
private final List<Lock> locks = new ArrayList<>(); | ||
private final Stack<Lock> locks = new Stack<>(); | ||
private final OmLockStats lockStats = new OmLockStats(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these fields intended to be static? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, as this is referred for each request, and can not be static. |
||
private OmLockStats.Type lockStatType = OmLockStats.Type.READ; | ||
private boolean readStatsType = true; | ||
|
||
public OmLockObject(OmLockInfo lockInfoProvider) { | ||
this.omLockInfo = lockInfoProvider; | ||
} | ||
|
||
public List<Lock> getLocks() { | ||
public Stack<Lock> getLocks() { | ||
return locks; | ||
} | ||
|
||
public OmLockStats getLockStats() { | ||
return lockStats; | ||
} | ||
|
||
public OmLockStats.Type getLockStatType() { | ||
return lockStatType; | ||
public void setReadStatsType(boolean readStatsType) { | ||
this.readStatsType = readStatsType; | ||
} | ||
|
||
public void setLockStatType(OmLockStats.Type lockStatType) { | ||
this.lockStatType = lockStatType; | ||
public boolean getReadStatsType() { | ||
return readStatsType; | ||
} | ||
|
||
public OmLockInfo getOmLockInfo() { | ||
return omLockInfo; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
while (!locks.empty()) { | ||
locks.pop().unlock(); | ||
} | ||
lockStatsEnd(lockStats, readStatsType); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -215,28 +208,16 @@ public long getWriteLockNanos() { | |
return writeLockNanos; | ||
} | ||
|
||
void add(long timeNanos, Type type) { | ||
switch (type) { | ||
case WAIT: | ||
waitLockNanos += timeNanos; | ||
break; | ||
case READ: | ||
readLockNanos += timeNanos; | ||
break; | ||
case WRITE: | ||
writeLockNanos += timeNanos; | ||
break; | ||
default: | ||
} | ||
void addWaitLockNanos(long timeNanos) { | ||
waitLockNanos += timeNanos; | ||
} | ||
|
||
void addReadLockNanos(long timeNanos) { | ||
readLockNanos += timeNanos; | ||
} | ||
|
||
/** | ||
* lock time stat type. | ||
*/ | ||
public enum Type { | ||
WAIT, | ||
READ, | ||
WRITE | ||
void addWriteLockNanos(long timeNanos) { | ||
writeLockNanos += timeNanos; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why return
OmLockObject
and require caller to pass it back tounlock
instead of usingAutocloseable
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defined OmLockObject as autoclosable, so based on usecase, it can be used with try-with-resouce.
We need return this object as it holds LockStats which is required to be set to HadoopRPC metrics with each request calling this interface.
This might be added to ozone metrics also to capture lock stats.