mirror of https://github.com/apache/kafka.git
MINOR: Refactor LockUtils and improve comments (follow up to KAFKA-19390) (#20131)
CI / build (push) Waiting to run
Details
CI / build (push) Waiting to run
Details
This PR performs a refactoring of LockUtils and improves inline comments, as a follow-up to https://github.com/apache/kafka/pull/19961. Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Jun Rao <junrao@gmail.com>
This commit is contained in:
parent
7ea32a0e93
commit
daece61a50
|
@ -18,7 +18,6 @@ package org.apache.kafka.server.util;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class providing helper methods for working with {@link Lock} objects.
|
* A utility class providing helper methods for working with {@link Lock} objects.
|
||||||
|
@ -35,50 +34,6 @@ public class LockUtils {
|
||||||
void run() throws E;
|
void run() throws E;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given {@link Supplier} within the context of the specified {@link Lock}.
|
|
||||||
* The lock is acquired before executing the supplier and released after the execution,
|
|
||||||
* ensuring that the lock is always released, even if an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the result returned by the supplier
|
|
||||||
* @param lock the lock to be acquired and released
|
|
||||||
* @param supplier the supplier to be executed within the lock context
|
|
||||||
* @return the result of the supplier
|
|
||||||
* @throws NullPointerException if either {@code lock} or {@code supplier} is null
|
|
||||||
*/
|
|
||||||
public static <T> T inLock(Lock lock, Supplier<T> supplier) {
|
|
||||||
Objects.requireNonNull(lock, "Lock must not be null");
|
|
||||||
Objects.requireNonNull(supplier, "Supplier must not be null");
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return supplier.get();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given {@link Runnable} within the context of the specified {@link Lock}.
|
|
||||||
* The lock is acquired before executing the runnable and released after the execution,
|
|
||||||
* ensuring that the lock is always released, even if an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param lock the lock to be acquired and released
|
|
||||||
* @param runnable the runnable to be executed within the lock context
|
|
||||||
* @throws NullPointerException if either {@code lock} or {@code runnable} is null
|
|
||||||
*/
|
|
||||||
public static void inLock(Lock lock, Runnable runnable) {
|
|
||||||
Objects.requireNonNull(lock, "Lock must not be null");
|
|
||||||
Objects.requireNonNull(runnable, "Runnable must not be null");
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
runnable.run();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the given {@link ThrowingSupplier} within the context of the specified {@link Lock}.
|
* Executes the given {@link ThrowingSupplier} within the context of the specified {@link Lock}.
|
||||||
* The lock is acquired before executing the supplier and released after the execution,
|
* The lock is acquired before executing the supplier and released after the execution,
|
||||||
|
@ -92,7 +47,7 @@ public class LockUtils {
|
||||||
* @throws E if an exception occurs during the execution of the supplier
|
* @throws E if an exception occurs during the execution of the supplier
|
||||||
* @throws NullPointerException if either {@code lock} or {@code supplier} is null
|
* @throws NullPointerException if either {@code lock} or {@code supplier} is null
|
||||||
*/
|
*/
|
||||||
public static <T, E extends Exception> T inLockThrows(Lock lock, ThrowingSupplier<T, E> supplier) throws E {
|
public static <T, E extends Exception> T inLock(Lock lock, ThrowingSupplier<T, E> supplier) throws E {
|
||||||
Objects.requireNonNull(lock, "Lock must not be null");
|
Objects.requireNonNull(lock, "Lock must not be null");
|
||||||
Objects.requireNonNull(supplier, "Supplier must not be null");
|
Objects.requireNonNull(supplier, "Supplier must not be null");
|
||||||
|
|
||||||
|
@ -115,7 +70,7 @@ public class LockUtils {
|
||||||
* @throws E if an exception occurs during the execution of the runnable
|
* @throws E if an exception occurs during the execution of the runnable
|
||||||
* @throws NullPointerException if either {@code lock} or {@code runnable} is null
|
* @throws NullPointerException if either {@code lock} or {@code runnable} is null
|
||||||
*/
|
*/
|
||||||
public static <E extends Exception> void inLockThrows(Lock lock, ThrowingRunnable<E> runnable) throws E {
|
public static <E extends Exception> void inLock(Lock lock, ThrowingRunnable<E> runnable) throws E {
|
||||||
Objects.requireNonNull(lock, "Lock must not be null");
|
Objects.requireNonNull(lock, "Lock must not be null");
|
||||||
Objects.requireNonNull(runnable, "Runnable must not be null");
|
Objects.requireNonNull(runnable, "Runnable must not be null");
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ import java.util.Objects;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The abstract index class which holds entry format agnostic methods.
|
* The abstract index class which holds entry format agnostic methods.
|
||||||
|
@ -48,7 +47,15 @@ public abstract class AbstractIndex implements Closeable {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AbstractIndex.class);
|
private static final Logger log = LoggerFactory.getLogger(AbstractIndex.class);
|
||||||
|
|
||||||
// Serializes all index operations that mutate internal state
|
// Serializes all index operations that mutate internal state.
|
||||||
|
// Readers do not need to acquire this lock because:
|
||||||
|
// 1) MappedByteBuffer provides direct access to the OS-level buffer cache,
|
||||||
|
// which allows concurrent reads in practice.
|
||||||
|
// 2) Clients only read committed data and are not affected by concurrent appends/truncates.
|
||||||
|
// In the rare case when the data is truncated, the follower could read inconsistent data.
|
||||||
|
// The follower has the logic to ignore the inconsistent data through crc and leader epoch.
|
||||||
|
// 3) Read and remap operations are coordinated via remapLock to ensure visibility of the
|
||||||
|
// underlying mmap.
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
// Allows concurrent read operations while ensuring exclusive access if the underlying mmap is changed
|
// Allows concurrent read operations while ensuring exclusive access if the underlying mmap is changed
|
||||||
private final ReentrantReadWriteLock remapLock = new ReentrantReadWriteLock();
|
private final ReentrantReadWriteLock remapLock = new ReentrantReadWriteLock();
|
||||||
|
@ -191,8 +198,8 @@ public abstract class AbstractIndex implements Closeable {
|
||||||
* @return a boolean indicating whether the size of the memory map and the underneath file is changed or not.
|
* @return a boolean indicating whether the size of the memory map and the underneath file is changed or not.
|
||||||
*/
|
*/
|
||||||
public boolean resize(int newSize) throws IOException {
|
public boolean resize(int newSize) throws IOException {
|
||||||
return inLockThrows(() ->
|
return inLock(() ->
|
||||||
inRemapWriteLockThrows(() -> {
|
inRemapWriteLock(() -> {
|
||||||
int roundedNewSize = roundDownToExactMultiple(newSize, entrySize());
|
int roundedNewSize = roundDownToExactMultiple(newSize, entrySize());
|
||||||
|
|
||||||
if (length == roundedNewSize) {
|
if (length == roundedNewSize) {
|
||||||
|
@ -258,7 +265,7 @@ public abstract class AbstractIndex implements Closeable {
|
||||||
* the file.
|
* the file.
|
||||||
*/
|
*/
|
||||||
public void trimToValidSize() throws IOException {
|
public void trimToValidSize() throws IOException {
|
||||||
inLockThrows(() -> {
|
inLock(() -> {
|
||||||
if (mmap != null) {
|
if (mmap != null) {
|
||||||
resize(entrySize() * entries);
|
resize(entrySize() * entries);
|
||||||
}
|
}
|
||||||
|
@ -282,10 +289,7 @@ public abstract class AbstractIndex implements Closeable {
|
||||||
// However, in some cases it can pause application threads(STW) for a long moment reading metadata from a physical disk.
|
// However, in some cases it can pause application threads(STW) for a long moment reading metadata from a physical disk.
|
||||||
// To prevent this, we forcefully cleanup memory mapping within proper execution which never affects API responsiveness.
|
// To prevent this, we forcefully cleanup memory mapping within proper execution which never affects API responsiveness.
|
||||||
// See https://issues.apache.org/jira/browse/KAFKA-4614 for the details.
|
// See https://issues.apache.org/jira/browse/KAFKA-4614 for the details.
|
||||||
inLockThrows(() ->
|
inLock(() -> inRemapWriteLock(this::safeForceUnmap));
|
||||||
inRemapWriteLockThrows(() -> {
|
|
||||||
safeForceUnmap();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -412,36 +416,28 @@ public abstract class AbstractIndex implements Closeable {
|
||||||
mmap.position(entries * entrySize());
|
mmap.position(entries * entrySize());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final <T> T inLock(Supplier<T> action) {
|
protected final <T, E extends Exception> T inLock(LockUtils.ThrowingSupplier<T, E> action) throws E {
|
||||||
return LockUtils.inLock(lock, action);
|
return LockUtils.inLock(lock, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void inLock(Runnable action) {
|
protected final <E extends Exception> void inLock(LockUtils.ThrowingRunnable<E> action) throws E {
|
||||||
LockUtils.inLock(lock, action);
|
LockUtils.inLock(lock, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final <T, E extends Exception> T inLockThrows(LockUtils.ThrowingSupplier<T, E> action) throws E {
|
protected final <T, E extends Exception> T inRemapReadLock(LockUtils.ThrowingSupplier<T, E> action) throws E {
|
||||||
return LockUtils.inLockThrows(lock, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <E extends Exception> void inLockThrows(LockUtils.ThrowingRunnable<E> action) throws E {
|
|
||||||
LockUtils.inLockThrows(lock, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <T> T inRemapReadLock(Supplier<T> action) {
|
|
||||||
return LockUtils.inLock(remapLock.readLock(), action);
|
return LockUtils.inLock(remapLock.readLock(), action);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void inRemapReadLock(Runnable action) {
|
protected final <E extends Exception> void inRemapReadLock(LockUtils.ThrowingRunnable<E> action) throws E {
|
||||||
LockUtils.inLock(remapLock.readLock(), action);
|
LockUtils.inLock(remapLock.readLock(), action);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final <T, E extends Exception> T inRemapWriteLockThrows(LockUtils.ThrowingSupplier<T, E> action) throws E {
|
protected final <T, E extends Exception> T inRemapWriteLock(LockUtils.ThrowingSupplier<T, E> action) throws E {
|
||||||
return LockUtils.inLockThrows(remapLock.writeLock(), action);
|
return LockUtils.inLock(remapLock.writeLock(), action);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final <E extends Exception> void inRemapWriteLockThrows(LockUtils.ThrowingRunnable<E> action) throws E {
|
protected final <E extends Exception> void inRemapWriteLock(LockUtils.ThrowingRunnable<E> action) throws E {
|
||||||
LockUtils.inLockThrows(remapLock.writeLock(), action);
|
LockUtils.inLock(remapLock.writeLock(), action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -215,7 +215,7 @@ public class TimeIndex extends AbstractIndex {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean resize(int newSize) throws IOException {
|
public boolean resize(int newSize) throws IOException {
|
||||||
return inLockThrows(() -> {
|
return inLock(() -> {
|
||||||
if (super.resize(newSize)) {
|
if (super.resize(newSize)) {
|
||||||
this.lastEntry = lastEntryFromIndexFile();
|
this.lastEntry = lastEntryFromIndexFile();
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue