Skip to content

Commit 0e2c03b

Browse files
authored
use the all-or-nothing semantics for filling directory entries (#4324)
1 parent 9ff4dda commit 0e2c03b

File tree

4 files changed

+68
-15
lines changed

4 files changed

+68
-15
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileHistoryCache.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -759,11 +759,13 @@ private String getCachedRevision(Repository repository, String revPath) {
759759
}
760760

761761
@Override
762-
public void fillLastHistoryEntries(List<DirectoryEntry> entries) {
762+
public boolean fillLastHistoryEntries(List<DirectoryEntry> entries) {
763763
if (entries == null) {
764-
return;
764+
return false;
765765
}
766766

767+
boolean ret = true;
768+
767769
for (DirectoryEntry directoryEntry : entries) {
768770
try {
769771
File file = directoryEntry.getFile();
@@ -780,11 +782,23 @@ public void fillLastHistoryEntries(List<DirectoryEntry> entries) {
780782
} else {
781783
LOGGER.log(Level.FINE, "cannot get last history entry for ''{0}''",
782784
directoryEntry.getFile());
785+
ret = false;
786+
break;
783787
}
784788
} catch (CacheException e) {
785789
LOGGER.log(Level.FINER, "cannot get last history entry for ''{0}''", directoryEntry.getFile());
790+
ret = false;
791+
break;
786792
}
787793
}
794+
795+
// Enforce the all-or-nothing semantics.
796+
if (!ret) {
797+
entries.forEach(e -> e.setDate(null));
798+
entries.forEach(e -> e.setDescription(null));
799+
}
800+
801+
return ret;
788802
}
789803

790804
@Override

opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryCache.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,14 @@ interface HistoryCache extends Cache {
9191
@Nullable String getLatestCachedRevision(Repository repository) throws CacheException;
9292

9393
/**
94-
* Get the last modified times for all files and subdirectories in the
95-
* specified directory.
94+
* Get the last modified times and descriptions for all files and subdirectories in the specified directory.
95+
* If any of the entries cannot be filled, these properties will be reset for all entries.
9696
*
9797
* @param entries list of {@link DirectoryEntry} instances
98+
* @return whether all directory entries were filled
9899
* @throws CacheException on error
99100
*/
100-
void fillLastHistoryEntries(List<DirectoryEntry> entries) throws CacheException;
101+
boolean fillLastHistoryEntries(List<DirectoryEntry> entries) throws CacheException;
101102

102103
/**
103104
* Clear entry for single file from history cache.

opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,7 @@ public boolean fillLastHistoryEntries(File directory, List<DirectoryEntry> entri
750750
return true;
751751
}
752752

753-
historyCache.fillLastHistoryEntries(entries);
754-
755-
return false;
753+
return !historyCache.fillLastHistoryEntries(entries);
756754
}
757755

758756
/**

opengrok-indexer/src/test/java/org/opengrok/indexer/history/FileHistoryCacheTest.java

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@
4343
import java.nio.file.Path;
4444
import java.nio.file.Paths;
4545
import java.nio.file.attribute.FileTime;
46-
import java.util.ArrayList;
4746
import java.util.Arrays;
4847
import java.util.Date;
4948
import java.util.Iterator;
5049
import java.util.LinkedList;
5150
import java.util.List;
5251
import java.util.Map;
5352
import java.util.Set;
53+
import java.util.stream.Collectors;
5454

5555
import org.apache.commons.lang3.time.DateUtils;
5656
import org.eclipse.jgit.api.Git;
@@ -1013,9 +1013,12 @@ void testGetLastHistoryEntry() throws Exception {
10131013
* getting history cache entries for directories.
10141014
*/
10151015
@Test
1016-
void testGetLastHistoryEntries() throws Exception {
1016+
void testFillLastHistoryEntries() throws Exception {
10171017
File repositoryRoot = new File(repositories.getSourceRoot(), "git");
10181018
Repository repository = RepositoryFactory.getRepository(repositoryRoot);
1019+
1020+
// Create non-empty directory without repository involvement. This will be used to check
1021+
// that fillLastHistoryEntries() does not attempt to get history entry for it.
10191022
File subDir = new File(repositoryRoot, "subdir");
10201023
assertTrue(subDir.mkdir());
10211024
File subFile = new File(subDir, "subfile.txt");
@@ -1031,14 +1034,51 @@ void testGetLastHistoryEntries() throws Exception {
10311034
assertNotNull(files);
10321035
assertTrue(files.length > 0);
10331036
assertTrue(Arrays.stream(files).anyMatch(File::isDirectory));
1034-
List<DirectoryEntry> directoryEntries = new ArrayList<>();
1035-
for (File file : files) {
1036-
directoryEntries.add(new DirectoryEntry(file));
1037-
}
1037+
List<DirectoryEntry> directoryEntries = Arrays.stream(files).map(DirectoryEntry::new).
1038+
collect(Collectors.toList());
10381039

1039-
spyCache.fillLastHistoryEntries(directoryEntries);
1040+
assertTrue(spyCache.fillLastHistoryEntries(directoryEntries));
10401041
Mockito.verify(spyCache, never()).getLastHistoryEntry(ArgumentMatchers.eq(subDir));
10411042

1043+
assertEquals(directoryEntries.size() - 3,
1044+
(int) directoryEntries.stream().filter(e -> e.getDate() != null).count());
1045+
assertEquals(directoryEntries.size(),
1046+
(int) directoryEntries.stream().filter(e -> e.getDescription() != null).count());
1047+
1048+
// Cleanup.
1049+
cache.clear(repository);
1050+
}
1051+
1052+
/**
1053+
* Test {@link FileHistoryCache#fillLastHistoryEntries(List)}, in particular that it
1054+
* returns {@code false} and resets date/descriptions if some entries cannot be filled.
1055+
*/
1056+
@Test
1057+
void testFillLastHistoryEntriesAllOrNothing() throws Exception {
1058+
File repositoryRoot = new File(repositories.getSourceRoot(), "git");
1059+
Repository repository = RepositoryFactory.getRepository(repositoryRoot);
1060+
1061+
// This file will be created without any repository involvement, therefore it will not be possible
1062+
// to get history entry for it. This should make fillLastHistoryEntries() to return false.
1063+
File subFile = new File(repositoryRoot, "file.txt");
1064+
assertFalse(subFile.exists());
1065+
assertTrue(subFile.createNewFile());
1066+
1067+
FileHistoryCache spyCache = Mockito.spy(cache);
1068+
spyCache.clear(repository);
1069+
History historyToStore = repository.getHistory(repositoryRoot);
1070+
spyCache.store(historyToStore, repository);
1071+
1072+
File[] files = repositoryRoot.listFiles();
1073+
assertNotNull(files);
1074+
assertTrue(files.length > 0);
1075+
List<DirectoryEntry> directoryEntries = Arrays.stream(files).map(DirectoryEntry::new).
1076+
collect(Collectors.toList());
1077+
1078+
assertFalse(spyCache.fillLastHistoryEntries(directoryEntries));
1079+
assertEquals(0, (int) directoryEntries.stream().filter(e -> e.getDate() != null).count());
1080+
assertEquals(0, (int) directoryEntries.stream().filter(e -> e.getDescription() != null).count());
1081+
10421082
// Cleanup.
10431083
cache.clear(repository);
10441084
}

0 commit comments

Comments
 (0)