Use separate FlusherRunner to fix NullPointerExceptions
Autoreindex feature tracks all index changes and writes corresponding
time stamps in 4 separate files. This fix implements 1 flush runner per
each index type. This change decreases the amount of file operations and
fixes NullPointerExceptions.
Original patch used a single FlashRunner instance and always updated 4
index types. During a startup there was a huge possibility that members
changeTs, accountTs, groupTs, projectTs where not initialized at the
same time. This led to NullPointerException. The fix spreads flow and
updates only 1 index per Listener invocation.
Bug: Issue 14030
Change-Id: I066f8ac009f5e410ccef3066e379c02c2790149c
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
index 0e0602a..8abdb59 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/IndexTs.java
@@ -50,7 +50,10 @@
private final Path dataDir;
private final ScheduledExecutorService exec;
- private final FlusherRunner flusher;
+ private final FlusherRunner changeFlusher;
+ private final FlusherRunner accountFlusher;
+ private final FlusherRunner groupFlusher;
+ private final FlusherRunner projectFlusher;
private final SchemaFactory<ReviewDb> schemaFactory;
private final ChangeFinder changeFinder;
private final CurrentRequestContext currCtx;
@@ -61,16 +64,11 @@
private volatile LocalDateTime projectTs;
class FlusherRunner implements Runnable {
+ private final AbstractIndexRestApiServlet.IndexName index;
@Override
public void run() {
- store(AbstractIndexRestApiServlet.IndexName.CHANGE, changeTs);
- store(AbstractIndexRestApiServlet.IndexName.ACCOUNT, accountTs);
- store(AbstractIndexRestApiServlet.IndexName.GROUP, groupTs);
- store(AbstractIndexRestApiServlet.IndexName.PROJECT, projectTs);
- }
-
- private void store(AbstractIndexRestApiServlet.IndexName index, LocalDateTime latestTs) {
+ LocalDateTime latestTs = getIndexTimeStamp();
Optional<LocalDateTime> currTs = getUpdateTs(index);
if (!currTs.isPresent() || latestTs.isAfter(currTs.get())) {
Path indexTsFile = dataDir.resolve(index.name().toLowerCase());
@@ -81,6 +79,25 @@
}
}
}
+
+ FlusherRunner(AbstractIndexRestApiServlet.IndexName index) {
+ this.index = index;
+ }
+
+ private LocalDateTime getIndexTimeStamp() {
+ switch (index) {
+ case CHANGE:
+ return changeTs;
+ case GROUP:
+ return groupTs;
+ case ACCOUNT:
+ return accountTs;
+ case PROJECT:
+ return projectTs;
+ default:
+ throw new IllegalArgumentException("Unsupported index " + index);
+ }
+ }
}
@Inject
@@ -92,7 +109,10 @@
CurrentRequestContext currCtx) {
this.dataDir = dataDir;
this.exec = queue.getDefaultQueue();
- this.flusher = new FlusherRunner();
+ this.changeFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.CHANGE);
+ this.accountFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.ACCOUNT);
+ this.groupFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.GROUP);
+ this.projectFlusher = new FlusherRunner(AbstractIndexRestApiServlet.IndexName.PROJECT);
this.schemaFactory = schemaFactory;
this.changeFinder = changeFinder;
this.currCtx = currCtx;
@@ -152,19 +172,22 @@
switch (index) {
case CHANGE:
changeTs = dateTime;
+ exec.execute(changeFlusher);
break;
case ACCOUNT:
accountTs = dateTime;
+ exec.execute(accountFlusher);
break;
case GROUP:
groupTs = dateTime;
+ exec.execute(groupFlusher);
break;
case PROJECT:
projectTs = dateTime;
+ exec.execute(projectFlusher);
break;
default:
throw new IllegalArgumentException("Unsupported index " + index);
}
- exec.execute(flusher);
}
}