Skip to content

Commit f5d1e22

Browse files
authored
Handle history cache failures (#4456)
fixes #747
1 parent a2cb0f9 commit f5d1e22

File tree

6 files changed

+336
-91
lines changed

6 files changed

+336
-91
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,17 @@ public void accept(String sinceRevision, Consumer<BoundaryChangesets.IdWithProgr
465465
RevWalk walk = new RevWalk(repository)) {
466466

467467
if (sinceRevision != null) {
468-
walk.markUninteresting(walk.lookupCommit(repository.resolve(sinceRevision)));
468+
ObjectId objId = repository.resolve(sinceRevision);
469+
if (objId == null) {
470+
throw new HistoryException("cannot resolve " + sinceRevision);
471+
}
472+
walk.markUninteresting(walk.lookupCommit(objId));
469473
}
470-
walk.markStart(walk.parseCommit(repository.resolve(Constants.HEAD)));
474+
ObjectId objId = repository.resolve(Constants.HEAD);
475+
if (objId == null) {
476+
throw new HistoryException("cannot resolve HEAD");
477+
}
478+
walk.markStart(walk.parseCommit(objId));
471479

472480
for (RevCommit commit : walk) {
473481
// Do not abbreviate the Id as this could cause AmbiguousObjectException in getHistory().

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

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
import java.util.List;
3636
import java.util.Map;
3737
import java.util.Objects;
38+
import java.util.Optional;
3839
import java.util.Set;
3940
import java.util.concurrent.ConcurrentHashMap;
4041
import java.util.concurrent.CountDownLatch;
42+
import java.util.concurrent.ExecutionException;
4143
import java.util.concurrent.ExecutorService;
4244
import java.util.concurrent.Executors;
4345
import java.util.concurrent.Future;
@@ -944,7 +946,7 @@ public void storeHistory(File file, History history) {
944946
}
945947
}
946948

947-
private void createHistoryCache(Repository repository, String sinceRevision) {
949+
private void createHistoryCache(Repository repository, String sinceRevision) throws CacheException, HistoryException {
948950
if (!repository.isHistoryEnabled()) {
949951
LOGGER.log(Level.INFO,
950952
"Skipping history cache creation for {0} and its subdirectories", repository);
@@ -955,26 +957,19 @@ private void createHistoryCache(Repository repository, String sinceRevision) {
955957
Statistics elapsed = new Statistics();
956958

957959
LOGGER.log(Level.INFO, "Creating history cache for {0}", repository);
958-
959-
try {
960-
repository.createCache(historyCache, sinceRevision);
961-
} catch (Exception e) {
962-
LOGGER.log(Level.WARNING,
963-
String.format("An error occurred while creating cache for %s", repository), e);
964-
}
965-
960+
repository.createCache(historyCache, sinceRevision);
966961
elapsed.report(LOGGER, String.format("Done history cache for %s", repository));
967962
} else {
968963
LOGGER.log(Level.WARNING,
969964
"Skipping creation of history cache for {0}: Missing SCM dependencies?", repository);
970965
}
971966
}
972967

973-
private void createHistoryCacheReal(Collection<Repository> repositories) {
968+
private Map<Repository, Optional<Exception>> createHistoryCacheReal(Collection<Repository> repositories) {
974969
if (repositories.isEmpty()) {
975970
LOGGER.log(Level.WARNING, "History cache is enabled however the list of repositories is empty. " +
976971
"Either specify the repositories in configuration or let the indexer scan them.");
977-
return;
972+
return Collections.emptyMap();
978973
}
979974

980975
Statistics elapsed = new Statistics();
@@ -999,32 +994,37 @@ private void createHistoryCacheReal(Collection<Repository> repositories) {
999994
}
1000995
}
1001996

1002-
LOGGER.log(Level.INFO, "Creating history cache for {0} repositories",
1003-
repos2process.size());
1004-
final CountDownLatch latch = new CountDownLatch(repos2process.size());
1005-
for (final Map.Entry<Repository, String> entry : repos2process.entrySet()) {
1006-
executor.submit(() -> {
1007-
try {
1008-
createHistoryCache(entry.getKey(), entry.getValue());
1009-
} catch (Exception ex) {
1010-
// We want to catch any exception since we are in thread.
1011-
LOGGER.log(Level.WARNING, "createHistoryCacheReal() got exception", ex);
1012-
} finally {
1013-
latch.countDown();
1014-
}
1015-
});
997+
LOGGER.log(Level.INFO, "Creating history cache for {0} repositories", repos2process.size());
998+
Map<Repository, Future<Optional<Exception>>> futures = new HashMap<>();
999+
try (Progress progress = new Progress(LOGGER, "repository invalidation", repos2process.size())) {
1000+
for (final Map.Entry<Repository, String> entry : repos2process.entrySet()) {
1001+
futures.put(entry.getKey(), executor.submit(() -> {
1002+
try {
1003+
createHistoryCache(entry.getKey(), entry.getValue());
1004+
} catch (Exception ex) { // We want to catch any exception since we are in thread.
1005+
LOGGER.log(Level.WARNING,
1006+
String.format("failed to create history cache for %s", entry.getKey()), ex);
1007+
return Optional.of(ex);
1008+
} finally {
1009+
progress.increment();
1010+
}
1011+
return Optional.empty();
1012+
}));
1013+
}
10161014
}
10171015

10181016
/*
10191017
* Wait until the history of all repositories is done. This is necessary
10201018
* since the next phase of generating index will need the history to
10211019
* be ready as it is recorded in Lucene index.
10221020
*/
1023-
try {
1024-
latch.await();
1025-
} catch (InterruptedException ex) {
1026-
LOGGER.log(Level.SEVERE, "latch exception", ex);
1027-
return;
1021+
Map<Repository, Optional<Exception>> results = new HashMap<>();
1022+
for (Map.Entry<Repository, Future<Optional<Exception>>> entry : futures.entrySet()) {
1023+
try {
1024+
results.put(entry.getKey(), entry.getValue().get());
1025+
} catch (InterruptedException | ExecutionException ex) {
1026+
results.put(entry.getKey(), Optional.of(ex));
1027+
}
10281028
}
10291029

10301030
// The cache has been populated. Now, optimize how it is stored on
@@ -1036,6 +1036,8 @@ private void createHistoryCacheReal(Collection<Repository> repositories) {
10361036
}
10371037
elapsed.report(LOGGER, "Done history cache for all repositories", "indexer.history.cache");
10381038
setHistoryIndexDone();
1039+
1040+
return results;
10391041
}
10401042

10411043
/**
@@ -1044,12 +1046,13 @@ private void createHistoryCacheReal(Collection<Repository> repositories) {
10441046
* internal map, e.g. via {@code setRepositories()} or {@code addRepositories()}.
10451047
*
10461048
* @param repositories list of repository paths
1049+
* @return map of repository to optional exception
10471050
*/
1048-
public void createHistoryCache(Collection<String> repositories) {
1051+
public Map<Repository, Optional<Exception>> createHistoryCache(Collection<String> repositories) {
10491052
if (!useHistoryCache()) {
1050-
return;
1053+
return Collections.emptyMap();
10511054
}
1052-
createHistoryCacheReal(getReposFromString(repositories));
1055+
return createHistoryCacheReal(getReposFromString(repositories));
10531056
}
10541057

10551058
/**
@@ -1161,13 +1164,14 @@ public List<String> removeAnnotationCache(Collection<RepositoryInfo> repositorie
11611164

11621165
/**
11631166
* Create the history cache for all repositories.
1167+
* @return map of repository to optional exception
11641168
*/
1165-
public void createHistoryCache() {
1169+
public Map<Repository, Optional<Exception>> createHistoryCache() {
11661170
if (!useHistoryCache()) {
1167-
return;
1171+
return Collections.emptyMap();
11681172
}
11691173

1170-
createHistoryCacheReal(repositories.values());
1174+
return createHistoryCacheReal(repositories.values());
11711175
}
11721176

11731177
/**

opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexDatabase.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
import java.util.HashSet;
4343
import java.util.List;
4444
import java.util.Map;
45+
import java.util.Map.Entry;
4546
import java.util.Objects;
47+
import java.util.Optional;
4648
import java.util.Set;
4749
import java.util.TreeMap;
4850
import java.util.concurrent.CopyOnWriteArrayList;
@@ -247,23 +249,57 @@ public IndexDatabase(Project project, IndexDownArgsFactory indexDownArgsFactory,
247249
LIVE_CHECK_FIELDS.add(QueryBuilder.PATH);
248250
}
249251

252+
public static void addIndexDatabaseForProject(@Nullable IndexDatabase db, Project project, List<IndexDatabase> dbs,
253+
Map<Repository, Optional<Exception>> historyCacheResults) throws IOException {
254+
255+
Map<Repository, Optional<Exception>> projectReposWithException = historyCacheResults.entrySet().
256+
stream().
257+
filter(e -> e.getValue().isPresent()).
258+
filter(e -> project.equals(Project.getProject(e.getKey().getDirectoryNameRelative()))).
259+
collect(Collectors.toMap(Entry::getKey, Entry::getValue));
260+
261+
if (projectReposWithException.isEmpty()) {
262+
dbs.add(db != null ? db : new IndexDatabase(project));
263+
} else {
264+
LOGGER.log(Level.SEVERE, "Failed to create history cache for some repositories of project {0}: {1}",
265+
new Object[]{project, projectReposWithException});
266+
}
267+
}
268+
269+
public static void addIndexDatabase(@Nullable IndexDatabase db, List<IndexDatabase> dbs,
270+
Map<Repository, Optional<Exception>> historyCacheResults) throws IOException {
271+
272+
Map<Repository, Optional<Exception>> reposWithException = historyCacheResults.entrySet().stream().
273+
filter(e -> e.getValue().isPresent()).
274+
collect(Collectors.toMap(Entry::getKey, Entry::getValue));
275+
276+
if (reposWithException.isEmpty()) {
277+
dbs.add(db != null ? db : new IndexDatabase());
278+
} else {
279+
LOGGER.log(Level.SEVERE, "Failed to create history cache for some repositories: {0}",
280+
reposWithException);
281+
}
282+
}
283+
250284
/**
251285
* Update the index database for all the projects.
252286
*
253287
* @param listener where to signal the changes to the database
288+
* @param historyCacheResults map of repository to optional exception
254289
* @throws IOException if an error occurs
255290
*/
256-
static CountDownLatch updateAll(IndexChangedListener listener) throws IOException {
291+
static CountDownLatch updateAll(IndexChangedListener listener,
292+
Map<Repository, Optional<Exception>> historyCacheResults) throws IOException {
257293

258294
RuntimeEnvironment env = RuntimeEnvironment.getInstance();
259295
List<IndexDatabase> dbs = new ArrayList<>();
260296

261297
if (env.hasProjects()) {
262298
for (Project project : env.getProjectList()) {
263-
dbs.add(new IndexDatabase(project));
299+
addIndexDatabaseForProject(null, project, dbs, historyCacheResults);
264300
}
265301
} else {
266-
dbs.add(new IndexDatabase());
302+
addIndexDatabase(null, dbs, historyCacheResults);
267303
}
268304

269305
IndexerParallelizer parallelizer = RuntimeEnvironment.getInstance().getIndexerParallelizer();

0 commit comments

Comments
 (0)