diff options
author | Nasser Grainawi <nasser.grainawi@linaro.org> | 2023-04-05 15:54:42 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2023-04-05 15:54:42 +0000 |
commit | 448a84e8dd4e6caa9acbff4ca000aae5be4aee56 (patch) | |
tree | 8df7d826afabc7286bef652f7b3e540552b84e9c | |
parent | e62dc9d4219baeb9496bb536ae536a019daa74c9 (diff) | |
parent | 5f5d9793eebe39f024090ad3bb462b2a23c4e5e8 (diff) |
Merge "Add a new core.usePerRequestRefCache setting" into stable-3.8
4 files changed, 147 insertions, 1 deletions
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 520855c34a..3726e54470 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -2059,6 +2059,16 @@ Values can be specified using standard time unit abbreviations (`ms`, `sec`, + Default is 1 hour. +[[core.usePerRequestRefCache]]core.usePerRequestRefCache:: ++ +Use a per request (currently per request thread) ref cache. The ref +cache uses JGit's SnapshottingRefDirectory to ensure that packed +refs are checked and potentially read at least once per request +(lazily) if needed. This helps reduce the overhead of checking if +the packed-refs file is outdated. ++ +Default is true. + [[dashboard]] === Section dashboard diff --git a/java/com/google/gerrit/server/cache/PerThreadRefDbCache.java b/java/com/google/gerrit/server/cache/PerThreadRefDbCache.java new file mode 100644 index 0000000000..a0526e1d17 --- /dev/null +++ b/java/com/google/gerrit/server/cache/PerThreadRefDbCache.java @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed 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 com.google.gerrit.server.git; + +import com.google.gerrit.server.cache.PerThreadCache; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import org.eclipse.jgit.internal.storage.file.RefDirectory; +import org.eclipse.jgit.lib.RefDatabase; + +/** A per request thread cache of RefDatabases by directory (Project). */ +public class PerThreadRefDbCache { + protected static final PerThreadCache.Key<PerThreadRefDbCache> REFDB_CACHE_KEY = + PerThreadCache.Key.create(PerThreadRefDbCache.class); + + public static RefDatabase getRefDatabase(File path, RefDatabase refDb) { + if (PerThreadCache.get() != null) { + return PerThreadCache.get() + .get(REFDB_CACHE_KEY, PerThreadRefDbCache::new) + .computeIfAbsent(path, p -> ((RefDirectory) refDb).createSnapshottingRefDirectory()); + } + return refDb; + } + + protected final Map<File, RefDatabase> refDbByRefsDir = new HashMap<>(); + + public RefDatabase computeIfAbsent( + File path, Function<? super File, ? extends RefDatabase> mappingFunction) { + return refDbByRefsDir.computeIfAbsent(path, mappingFunction); + } +} diff --git a/java/com/google/gerrit/server/git/DynamicRefDbRepository.java b/java/com/google/gerrit/server/git/DynamicRefDbRepository.java new file mode 100644 index 0000000000..2e81ad4c28 --- /dev/null +++ b/java/com/google/gerrit/server/git/DynamicRefDbRepository.java @@ -0,0 +1,83 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed 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 com.google.gerrit.server.git; + +import java.io.File; +import java.io.IOException; +import java.util.function.BiFunction; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryCache; +import org.eclipse.jgit.util.FS; + +/** A FileRepository with a dynamic RefDatabase supplied via a BiFunction. */ +public class DynamicRefDbRepository extends FileRepository { + public static class FileKey extends RepositoryCache.FileKey { + private BiFunction<File, RefDatabase, RefDatabase> refDatabaseSupplier; + + public static FileKey lenient( + File directory, FS fs, BiFunction<File, RefDatabase, RefDatabase> refDatabaseSupplier) { + final File gitdir = resolve(directory, fs); + return new FileKey(gitdir != null ? gitdir : directory, fs, refDatabaseSupplier); + } + + private final FS fs; + + /** + * @param directory exact location of the repository. + * @param fs the file system abstraction which will be necessary to perform certain file system + * operations. + */ + public FileKey( + File directory, FS fs, BiFunction<File, RefDatabase, RefDatabase> refDatabaseSupplier) { + super(canonical(directory), fs); + this.fs = fs; + this.refDatabaseSupplier = refDatabaseSupplier; + } + + @Override + public Repository open(boolean mustExist) throws IOException { + if (mustExist && !isGitRepository(getFile(), fs)) + throw new RepositoryNotFoundException(getFile()); + return new DynamicRefDbRepository(getFile(), refDatabaseSupplier); + } + + private static File canonical(File path) { + try { + return path.getCanonicalFile(); + } catch (IOException e) { + return path.getAbsoluteFile(); + } + } + } + + private final File path; + private final BiFunction<File, RefDatabase, RefDatabase> refDatabaseSupplier; + + public DynamicRefDbRepository( + File path, BiFunction<File, RefDatabase, RefDatabase> refDatabaseSupplier) + throws IOException { + super(path); + this.path = path; + this.refDatabaseSupplier = refDatabaseSupplier; + } + + @Override + public RefDatabase getRefDatabase() { + return refDatabaseSupplier.apply(path, super.getRefDatabase()); + } +} diff --git a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index 57d37fae08..5cf7de52fd 100644 --- a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java @@ -112,6 +112,7 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { private final Path basePath; private final Map<Project.NameKey, FileKey> fileKeyByProject = new ConcurrentHashMap<>(); + private final boolean usePerRequestRefCache; @Inject LocalDiskRepositoryManager(SitePaths site, @GerritServerConfig Config cfg) { @@ -119,6 +120,7 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { if (basePath == null) { throw new IllegalStateException("gerrit.basePath must be configured"); } + usePerRequestRefCache = cfg.getBoolean("core", null, "usePerRequestRefCache", true); } /** @@ -168,7 +170,13 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - FileKey location = FileKey.lenient(getBasePath(name).resolve(name.get()).toFile(), FS.DETECTED); + FileKey location = + usePerRequestRefCache + ? DynamicRefDbRepository.FileKey.lenient( + getBasePath(name).resolve(name.get()).toFile(), + FS.DETECTED, + (path, refDb) -> PerThreadRefDbCache.getRefDatabase(path, refDb)) + : FileKey.lenient(getBasePath(name).resolve(name.get()).toFile(), FS.DETECTED); try { Repository repo = RepositoryCache.open(location); fileKeyByProject.put(name, location); |