[ https://issues.apache.org/jira/browse/HDFS-17384?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17908255#comment-17908255 ]
ASF GitHub Bot commented on HDFS-17384: --------------------------------------- ZanderXu commented on code in PR #6762: URL: https://github.com/apache/hadoop/pull/6762#discussion_r1897678303 ########## hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/fgl/FineGrainedFSNamesystemLock.java: ########## @@ -0,0 +1,300 @@ +/** + * 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.hadoop.hdfs.server.namenode.fgl; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hdfs.server.namenode.FSNamesystemLock; +import org.apache.hadoop.metrics2.lib.MutableRatesWithAggregation; + +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * Splitting the global FSN lock into FSLock and BMLock. + * FSLock is used to protect directory tree-related operations. + * BMLock is used to protect block-related and dn-related operations. + * The lock order should be: FSLock,BMLock. + */ +public class FineGrainedFSNamesystemLock implements FSNLockManager { + private final FSNamesystemLock fsLock; + private final FSNamesystemLock bmLock; + + public FineGrainedFSNamesystemLock(Configuration conf, MutableRatesWithAggregation aggregation) { + this.fsLock = new FSNamesystemLock(conf, "FS", aggregation); + this.bmLock = new FSNamesystemLock(conf, "BM", aggregation); + } + + @Override + public void readLock(FSNamesystemLockMode lockMode) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.fsLock.readLock(); + this.bmLock.readLock(); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.readLock(); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.readLock(); + } + } + + public void readLockInterruptibly(FSNamesystemLockMode lockMode) throws InterruptedException { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.fsLock.readLockInterruptibly(); + try { + this.bmLock.readLockInterruptibly(); + } catch (InterruptedException e) { + // The held FSLock should be released if the current thread is interrupted + // while acquiring the BMLock. + this.fsLock.readUnlock("BMReadLockInterruptiblyFailed"); + throw e; + } + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.readLockInterruptibly(); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.readLockInterruptibly(); + } + } + + @Override + public void readUnlock(FSNamesystemLockMode lockMode, String opName) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.bmLock.readUnlock(opName); + this.fsLock.readUnlock(opName); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.readUnlock(opName); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.readUnlock(opName); + } + } + + public void readUnlock(FSNamesystemLockMode lockMode, String opName, + Supplier<String> lockReportInfoSupplier) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.bmLock.readUnlock(opName, lockReportInfoSupplier); + this.fsLock.readUnlock(opName, lockReportInfoSupplier); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.readUnlock(opName, lockReportInfoSupplier); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.readUnlock(opName, lockReportInfoSupplier); + } + } + + @Override + public void writeLock(FSNamesystemLockMode lockMode) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.fsLock.writeLock(); + this.bmLock.writeLock(); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.writeLock(); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.writeLock(); + } + } + + @Override + public void writeUnlock(FSNamesystemLockMode lockMode, String opName) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.bmLock.writeUnlock(opName); + this.fsLock.writeUnlock(opName); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.writeUnlock(opName); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.writeUnlock(opName); + } + } + + @Override + public void writeUnlock(FSNamesystemLockMode lockMode, String opName, + boolean suppressWriteLockReport) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.bmLock.writeUnlock(opName, suppressWriteLockReport); + this.fsLock.writeUnlock(opName, suppressWriteLockReport); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.writeUnlock(opName, suppressWriteLockReport); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.writeUnlock(opName, suppressWriteLockReport); + } + } + + public void writeUnlock(FSNamesystemLockMode lockMode, String opName, + Supplier<String> lockReportInfoSupplier) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.bmLock.writeUnlock(opName, lockReportInfoSupplier); + this.fsLock.writeUnlock(opName, lockReportInfoSupplier); + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.writeUnlock(opName, lockReportInfoSupplier); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.writeUnlock(opName, lockReportInfoSupplier); + } + } + + @Override + public void writeLockInterruptibly(FSNamesystemLockMode lockMode) + throws InterruptedException { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + this.fsLock.writeLockInterruptibly(); + try { + this.bmLock.writeLockInterruptibly(); + } catch (InterruptedException e) { + // The held FSLock should be released if the current thread is interrupted + // while acquiring the BMLock. + this.fsLock.writeUnlock("BMWriteLockInterruptiblyFailed"); + throw e; + } + } else if (lockMode.equals(FSNamesystemLockMode.FS)) { + this.fsLock.writeLockInterruptibly(); + } else if (lockMode.equals(FSNamesystemLockMode.BM)) { + this.bmLock.writeLockInterruptibly(); + } + } + + @Override + public boolean hasWriteLock(FSNamesystemLockMode lockMode) { + if (lockMode.equals(FSNamesystemLockMode.GLOBAL)) { + if (this.fsLock.isWriteLockedByCurrentThread()) { + // The bm writeLock should be held by the current thread. + assert this.bmLock.isWriteLockedByCurrentThread(); Review Comment: Thanks, @zhengchenyu , for your review. Regarding point (2), are you suggesting that the `hasWriteLock(RwLockMode lockMode)` method should strictly return `true` or `false` and never throw an `AssertionError`? Indeed, `hasWriteLock(RwLockMode lockMode)` is intended to always return `true` or `false`. However, I included the check to identify potential issue with lock ordering, because the caller often expects to receive a `true` result in NameNode. If this additional check is deemed unnecessary, the code could simply be: ``` if (lockMode.equals(RwLockMode.GLOBAL)) { return this.fsLock.isWriteLockedByCurrentThread() && this.bmLock.isWriteLockedByCurrentThread(); } ``` Looking forward your ideas, thanks. > [FGL] Replace the global lock with global FS Lock and global BM lock > -------------------------------------------------------------------- > > Key: HDFS-17384 > URL: https://issues.apache.org/jira/browse/HDFS-17384 > Project: Hadoop HDFS > Issue Type: Improvement > Reporter: ZanderXu > Assignee: ZanderXu > Priority: Major > Labels: FGL, pull-request-available > > First, we can replace the current global lock with two locks, global FS lock > and global BM lock. > The global FS lock is used to make directory tree-related operations > thread-safe. > The global BM lock is used to make block-related operations and DN-related > operations thread-safe. > > For some operations involving both directory tree and block or DN, the global > FS lock and the global BM lock are acquired. > > The lock order should be: > * The global FS lock > * The global BM lock > > There are some special requirements for this ticket. > * End-user can choose to use global lock or fine-grained lock through > configuration. > * Try not to modify the current implementation logic as much as possible. -- This message was sent by Atlassian Jira (v8.20.10#820010) --------------------------------------------------------------------- To unsubscribe, e-mail: hdfs-issues-unsubscr...@hadoop.apache.org For additional commands, e-mail: hdfs-issues-h...@hadoop.apache.org