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); } }