Skip to content

Commit ac5146f

Browse files
committed
FileReftableStack: use FileSnapshot to detect modification
Reading file attributes is faster than reading file content hence use FileSnapshot to speedup detecting if FileReftableStack is up-to-date. Introduce new option "core.trustTablesListStat" allowing to configure if we can trust file attributes of the "tables.list" file to speedup detection of file modifications. This file is used to store the list of filenames of the files storing Reftables in FileReftableDatabase. If this option is set to "ALWAYS" we trust File attributes and use them to speedup detection of file modifications. If set to "NEVER" the content of the "tables.list" file is always read unconditionally. This can help to avoid caching issues on some filesystems. If set to "AFTER_OPEN" we will open a FileInputStream to refresh File attributes of the "tables.list" file before relying on the refreshed File attributes to detect modifications. This works on some NFS filesystems and is faster than using "NEVER". Change-Id: I3e288d90fb07edf4fa2a03c707a333b26f0c458d
1 parent 1ff9c2a commit ac5146f

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

Documentation/config-options.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ For details on native git options see also the official [git config documentatio
5858
| ~~`core.trustFolderStat`~~ | `true` | ⃞ | __Deprecated__, use `core.trustStat` instead. If set to `true` translated to `core.trustStat=always`, if `false` translated to `core.trustStat=never`, see below. If both `core.trustFolderStat` and `core.trustStat` are configured then `trustStat` takes precedence and `trustFolderStat` is ignored. |
5959
| `core.trustLooseRefStat` | `inherit` | ⃞ | Whether to trust the file attributes of loose refs and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `trustStat`. |
6060
| `core.trustPackedRefsStat` | `inherit` | ⃞ | Whether to trust the file attributes of the packed-refs file. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
61+
| `core.trustTablesListStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `tables.list` file used by the reftable ref storage backend to store the list of reftable filenames. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. The reftable backend is used if `extensions.refStorage = reftable`. |
6162
| `core.trustLooseObjectStat` | `inherit` | ⃞ | Whether to trust the file attributes of the loose object file and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
6263
| `core.trustPackStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `objects/pack` directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
63-
| `core.trustStat` | `always` | ⃞ | Global option to configure whether to trust file attributes (Java equivalent of stat command on Unix) of files storing git objects. Can be overridden for specific files by configuring `core.trustLooseRefStat, core.trustPackedRefsStat, core.trustLooseObjectStat, core.trustPackStat`. If `never` JGit will ignore the file attributes of the file and always read it. If `always` JGit will trust the file attributes and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, but file attributes are only considered *after* the file itself and any transient parent directories have been opened and closed. An open/close of the file/directory is known to refresh its file attributes, at least on some NFS clients. |
64+
| `core.trustStat` | `always` | ⃞ | Global option to configure whether to trust file attributes (Java equivalent of stat command on Unix) of files storing git objects. Can be overridden for specific files by configuring `core.trustLooseRefStat, core.trustPackedRefsStat, core.trustLooseObjectStat, core.trustPackStat,core.trustTablesListStat`. If `never` JGit will ignore the file attributes of the file and always read it. If `always` JGit will trust the file attributes and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, but file attributes are only considered *after* the file itself and any transient parent directories have been opened and closed. An open/close of the file/directory is known to refresh its file attributes, at least on some NFS clients. |
6465
| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. |
6566

6667
## __fetch__ options

org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@
1818
import java.io.FileNotFoundException;
1919
import java.io.FileOutputStream;
2020
import java.io.IOException;
21+
import java.io.InputStream;
2122
import java.io.InputStreamReader;
2223
import java.nio.file.Files;
24+
import java.nio.file.NoSuchFileException;
2325
import java.nio.file.StandardCopyOption;
2426
import java.security.SecureRandom;
2527
import java.util.ArrayList;
2628
import java.util.Comparator;
2729
import java.util.List;
2830
import java.util.Map;
2931
import java.util.Optional;
32+
import java.util.concurrent.atomic.AtomicReference;
3033
import java.util.function.Supplier;
3134
import java.util.stream.Collectors;
3235

@@ -39,6 +42,8 @@
3942
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
4043
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
4144
import org.eclipse.jgit.lib.Config;
45+
import org.eclipse.jgit.lib.CoreConfig;
46+
import org.eclipse.jgit.lib.CoreConfig.TrustStat;
4247
import org.eclipse.jgit.util.FileUtils;
4348
import org.eclipse.jgit.util.SystemReader;
4449

@@ -59,6 +64,9 @@ private static class StackEntry {
5964

6065
private List<StackEntry> stack;
6166

67+
private AtomicReference<FileSnapshot> snapshot = new AtomicReference<>(
68+
FileSnapshot.DIRTY);
69+
6270
private long lastNextUpdateIndex;
6371

6472
private final File stackPath;
@@ -98,6 +106,8 @@ static class CompactionStats {
98106

99107
private final CompactionStats stats;
100108

109+
private final TrustStat trustTablesListStat;
110+
101111
/**
102112
* Creates a stack corresponding to the list of reftables in the argument
103113
*
@@ -126,6 +136,8 @@ public FileReftableStack(File stackPath, File reftableDir,
126136
reload();
127137

128138
stats = new CompactionStats();
139+
trustTablesListStat = configSupplier.get().get(CoreConfig.KEY)
140+
.getTrustTablesListStat();
129141
}
130142

131143
CompactionStats getStats() {
@@ -272,8 +284,9 @@ public interface Writer {
272284
}
273285

274286
private List<String> readTableNames() throws IOException {
287+
FileSnapshot old;
275288
List<String> names = new ArrayList<>(stack.size() + 1);
276-
289+
old = snapshot.get();
277290
try (BufferedReader br = new BufferedReader(
278291
new InputStreamReader(new FileInputStream(stackPath), UTF_8))) {
279292
String line;
@@ -282,8 +295,10 @@ private List<String> readTableNames() throws IOException {
282295
names.add(line);
283296
}
284297
}
298+
snapshot.compareAndSet(old, FileSnapshot.save(stackPath));
285299
} catch (FileNotFoundException e) {
286300
// file isn't there: empty repository.
301+
snapshot.compareAndSet(old, FileSnapshot.MISSING_FILE);
287302
}
288303
return names;
289304
}
@@ -294,9 +309,28 @@ private List<String> readTableNames() throws IOException {
294309
* on IO problem
295310
*/
296311
boolean isUpToDate() throws IOException {
297-
// We could use FileSnapshot to avoid reading the file, but the file is
298-
// small so it's probably a minor optimization.
299312
try {
313+
switch (trustTablesListStat) {
314+
case NEVER:
315+
break;
316+
case AFTER_OPEN:
317+
try (InputStream stream = Files
318+
.newInputStream(stackPath.toPath())) {
319+
// open the tables.list file to refresh attributes (on some
320+
// NFS clients)
321+
} catch (FileNotFoundException | NoSuchFileException e) {
322+
// ignore
323+
}
324+
//$FALL-THROUGH$
325+
case ALWAYS:
326+
if (!snapshot.get().isModified(stackPath)) {
327+
return true;
328+
}
329+
break;
330+
case INHERIT:
331+
// only used in CoreConfig internally
332+
throw new IllegalStateException();
333+
}
300334
List<String> names = readTableNames();
301335
if (names.size() != stack.size()) {
302336
return false;

org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,14 @@ public final class ConfigConstants {
10451045
* @since 7.2
10461046
*/
10471047
public static final String CONFIG_KEY_TRUST_LOOSE_OBJECT_STAT = "trustLooseObjectStat";
1048+
1049+
/**
1050+
* The "trustTablesListStat" key
1051+
*
1052+
* @since 7.2
1053+
*/
1054+
public static final String CONFIG_KEY_TRUST_TABLESLIST_STAT = "trustTablesListStat";
1055+
10481056
/**
10491057
* The "pack.preserveOldPacks" key
10501058
*

org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ public enum TrustStat {
215215

216216
private final TrustStat trustLooseObjectStat;
217217

218+
private final TrustStat trustTablesListStat;
219+
218220
/**
219221
* Options for symlink handling
220222
*
@@ -268,6 +270,7 @@ public enum HideDotFiles {
268270
trustLooseRefStat = parseTrustLooseRefStat(rc);
269271
trustPackStat = parseTrustPackFileStat(rc);
270272
trustLooseObjectStat = parseTrustLooseObjectFileStat(rc);
273+
trustTablesListStat = parseTablesListStat(rc);
271274
}
272275

273276
private static TrustStat parseTrustStat(Config rc) {
@@ -318,6 +321,13 @@ private TrustStat inheritParseTrustStat(Config rc, String key) {
318321
return t == TrustStat.INHERIT ? trustStat : t;
319322
}
320323

324+
private TrustStat parseTablesListStat(Config rc) {
325+
TrustStat t = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
326+
ConfigConstants.CONFIG_KEY_TRUST_TABLESLIST_STAT,
327+
TrustStat.INHERIT);
328+
return t == TrustStat.INHERIT ? trustStat : t;
329+
}
330+
321331
/**
322332
* Get the compression level to use when storing loose objects
323333
*
@@ -418,4 +428,18 @@ public TrustStat getTrustPackStat() {
418428
public TrustStat getTrustLooseObjectStat() {
419429
return trustLooseObjectStat;
420430
}
431+
432+
/**
433+
* Get how far we can trust file attributes of the "tables.list" file which
434+
* is used to store the list of filenames of the files storing
435+
* {@link org.eclipse.jgit.internal.storage.reftable.Reftable}s in
436+
* {@link org.eclipse.jgit.internal.storage.file.FileReftableDatabase}.
437+
*
438+
* @return how far we can trust file attributes of the "tables.list" file.
439+
*
440+
* @since 7.2
441+
*/
442+
public TrustStat getTrustTablesListStat() {
443+
return trustTablesListStat;
444+
}
421445
}

0 commit comments

Comments
 (0)