Skip to content

Commit 22e1b30

Browse files
authored
make HistoryGuru#getLastHistoryEntry more efficient (#4271)
1 parent 138b80d commit 22e1b30

File tree

6 files changed

+96
-17
lines changed

6 files changed

+96
-17
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ static History readHistory(File cacheFile, Repository repository) throws IOExcep
209209
return history;
210210
}
211211

212+
static HistoryEntry readLastHistoryEntry(File cacheFile) throws IOException {
213+
SmileFactory factory = new SmileFactory();
214+
ObjectMapper mapper = new SmileMapper();
215+
HistoryEntry historyEntry = null;
216+
217+
try (SmileParser parser = factory.createParser(cacheFile)) {
218+
parser.setCodec(mapper);
219+
Iterator<HistoryEntry> historyEntryIterator = parser.readValuesAs(HistoryEntry.class);
220+
if (historyEntryIterator.hasNext()) {
221+
historyEntry = historyEntryIterator.next();
222+
}
223+
}
224+
225+
return historyEntry;
226+
}
227+
212228
/**
213229
* Write serialized object to file.
214230
* @param history {@link History} instance to be stored
@@ -653,6 +669,27 @@ public History get(File file, Repository repository, boolean withFiles) throws C
653669
return null;
654670
}
655671

672+
@Override
673+
public HistoryEntry getLastHistoryEntry(File file) throws CacheException {
674+
if (isUpToDate(file)) {
675+
File cacheFile = getCachedFile(file);
676+
try {
677+
if (fileHistoryCacheHits != null) {
678+
fileHistoryCacheHits.increment();
679+
}
680+
return readLastHistoryEntry(cacheFile);
681+
} catch (Exception e) {
682+
LOGGER.log(Level.WARNING, String.format("Error when reading cache file '%s'", cacheFile), e);
683+
}
684+
}
685+
686+
if (fileHistoryCacheMisses != null) {
687+
fileHistoryCacheMisses.increment();
688+
}
689+
690+
return null;
691+
}
692+
656693
/**
657694
* @param file the file to check
658695
* @return {@code true} if the cache is up-to-date for the file, {@code false} otherwise

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
2222
*/
2323
package org.opengrok.indexer.history;
2424

@@ -43,6 +43,14 @@ interface HistoryCache extends Cache {
4343
*/
4444
History get(File file, @Nullable Repository repository, boolean withFiles) throws CacheException;
4545

46+
/**
47+
* Retrieve last (newest) history entry for the given file from the cache.
48+
*
49+
* @param file The file to retrieve history for
50+
* @throws CacheException if the history cache cannot be read
51+
*/
52+
HistoryEntry getLastHistoryEntry(File file) throws CacheException;
53+
4654
/**
4755
* Store the history for a repository.
4856
*

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

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -418,32 +418,39 @@ private History getHistoryFromCache(File file, Repository repository, boolean wi
418418
return null;
419419
}
420420

421+
@Nullable
422+
private HistoryEntry getLastHistoryEntryFromCache(File file, Repository repository) throws CacheException {
423+
424+
if (useHistoryCache() && historyCache.supportsRepository(repository)) {
425+
return historyCache.getLastHistoryEntry(file);
426+
}
427+
428+
return null;
429+
}
430+
421431
/**
422432
* Get last {@link HistoryEntry} for a file. First, try to retrieve it from the cache.
423433
* If that fails, fallback to the repository method.
424434
* @param file file to get the history entry for
425435
* @param ui is the request coming from the UI
436+
* @param fallback whether to perform fallback to repository method if cache retrieval fails
426437
* @return last (newest) history entry for given file or {@code null}
427438
* @throws HistoryException if history retrieval failed
428439
*/
429440
@Nullable
430-
public HistoryEntry getLastHistoryEntry(File file, boolean ui) throws HistoryException {
441+
public HistoryEntry getLastHistoryEntry(File file, boolean ui, boolean fallback) throws HistoryException {
431442
Statistics statistics = new Statistics();
432443
LOGGER.log(Level.FINEST, "started retrieval of last history entry for ''{0}''", file);
433444
final File dir = file.isDirectory() ? file : file.getParentFile();
434445
final Repository repository = getRepository(dir);
435446

436-
History history;
437447
try {
438-
history = getHistoryFromCache(file, repository, false);
439-
if (history != null) {
440-
HistoryEntry lastHistoryEntry = history.getLastHistoryEntry();
441-
if (lastHistoryEntry != null) {
442-
statistics.report(LOGGER, Level.FINEST,
443-
String.format("got latest history entry %s for ''%s'' from history cache",
444-
lastHistoryEntry, file), "history.entry.latest");
445-
return lastHistoryEntry;
446-
}
448+
HistoryEntry lastHistoryEntry = getLastHistoryEntryFromCache(file, repository);
449+
if (lastHistoryEntry != null) {
450+
statistics.report(LOGGER, Level.FINEST,
451+
String.format("got latest history entry %s for ''%s'' from history cache",
452+
lastHistoryEntry, file), "history.entry.latest");
453+
return lastHistoryEntry;
447454
}
448455
} catch (CacheException e) {
449456
LOGGER.log(Level.FINER,
@@ -452,6 +459,14 @@ public HistoryEntry getLastHistoryEntry(File file, boolean ui) throws HistoryExc
452459
e.getMessage());
453460
}
454461

462+
if (!fallback) {
463+
statistics.report(LOGGER, Level.FINEST,
464+
String.format("cannot retrieve the last history entry for ''%s'' in %s because fallback to" +
465+
"repository method is disabled",
466+
file, repository), "history.entry.latest");
467+
return null;
468+
}
469+
455470
if (!isRepoHistoryEligible(repository, file, ui)) {
456471
statistics.report(LOGGER, Level.FINEST,
457472
String.format("cannot retrieve the last history entry for ''%s'' in %s because of settings",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public static String getLastRevFromIndex(File file) {
113113

114114
@Nullable
115115
private static String getLastRevFromHistory(File file) throws HistoryException {
116-
HistoryEntry he = HistoryGuru.getInstance().getLastHistoryEntry(file, true);
116+
HistoryEntry he = HistoryGuru.getInstance().getLastHistoryEntry(file, true, true);
117117
if (he != null) {
118118
return he.getRevision();
119119
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,4 +983,23 @@ void testStoreAndTryToGetIgnored() throws Exception {
983983
retrievedHistory = cache.get(makefile, repo, true);
984984
assertNotNull(retrievedHistory, "history for Makefile should not be null");
985985
}
986+
987+
@Test
988+
void testGetLastHistoryEntry() throws Exception {
989+
File repositoryRoot = new File(repositories.getSourceRoot(), "git");
990+
991+
Repository repository = RepositoryFactory.getRepository(repositoryRoot);
992+
993+
cache.clear(repository);
994+
File sourceFile = new File(repositoryRoot, "main.c");
995+
assertTrue(repository.getHistory(sourceFile).getHistoryEntries().size() > 1);
996+
assertTrue(sourceFile.exists());
997+
assertNull(cache.getLastHistoryEntry(sourceFile));
998+
999+
History historyToStore = repository.getHistory(repositoryRoot);
1000+
cache.store(historyToStore, repository);
1001+
HistoryEntry historyEntry = cache.getLastHistoryEntry(sourceFile);
1002+
assertNotNull(historyEntry);
1003+
assertEquals("aa35c25882b9a60a97758e0ceb276a3f8cb4ae3a", historyEntry.getRevision());
1004+
}
9861005
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ void testScanningDepth() throws IOException {
377377
void testGetLastHistoryEntryNonexistent() throws Exception {
378378
HistoryGuru instance = HistoryGuru.getInstance();
379379
File file = new File("/nonexistent");
380-
assertNull(instance.getLastHistoryEntry(file, true));
380+
assertNull(instance.getLastHistoryEntry(file, true, true));
381381
}
382382

383383
@ParameterizedTest
@@ -391,9 +391,9 @@ void testGetLastHistoryEntryVsIndexer(boolean isIndexerParam) throws HistoryExce
391391
File file = new File(repository.getSourceRoot(), "git");
392392
assertTrue(file.exists());
393393
if (isIndexerParam) {
394-
assertThrows(IllegalStateException.class, () -> instance.getLastHistoryEntry(file, true));
394+
assertThrows(IllegalStateException.class, () -> instance.getLastHistoryEntry(file, true, true));
395395
} else {
396-
assertNotNull(instance.getLastHistoryEntry(file, true));
396+
assertNotNull(instance.getLastHistoryEntry(file, true, true));
397397
}
398398
env.setIndexer(isIndexer);
399399
env.setTagsEnabled(isTagsEnabled);
@@ -417,7 +417,7 @@ void testGetLastHistoryEntryRepoHistoryDisabled() throws Exception {
417417

418418
assertNotNull(repository);
419419
repository.setHistoryEnabled(false);
420-
assertNull(instance.getLastHistoryEntry(file, false));
420+
assertNull(instance.getLastHistoryEntry(file, false, true));
421421

422422
// cleanup
423423
repository.setHistoryEnabled(true);

0 commit comments

Comments
 (0)