diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java
index fbae630a0..ee435f219 100644
--- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java
+++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java
@@ -77,7 +77,7 @@ public class AclTracker extends ActivatableTracker
private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32;
private static final long DEFAULT_ACL_TRACKER_TIMESTEP = TIME_STEP_1_HR_IN_MS;
- private static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L;
+ protected static final long INITIAL_MAX_ACL_CHANGE_SET_ID = 2000L;
private static final int MAX_NUMBER_OF_ACL_CHANGE_SETS = 2000;
private static final long MAX_TIME_STEP = TIME_STEP_32_DAYS_IN_MS;
@@ -405,17 +405,23 @@ public class AclTracker extends ActivatableTracker
/**
* Checks the first and last TX time
*/
- private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException
+ protected void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException
{
- AclChangeSets firstChangeSets = null;
+ if (state.getLastGoodChangeSetCommitTimeInIndex() != 0 && state.isCheckedFirstAclTransactionTime() && state.isCheckedLastAclTransactionTime())
+ {
+ // Verification done previously.
+ return;
+ }
+
+ AclChangeSets firstChangeSets = client.getAclChangeSets(null, 0L,
+ null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1);
+
if (state.getLastGoodChangeSetCommitTimeInIndex() == 0)
{
state.setCheckedLastAclTransactionTime(true);
state.setCheckedFirstAclTransactionTime(true);
LOGGER.info("[CORE {}] - No acl transactions found - no verification required", coreName);
-
- firstChangeSets = client.getAclChangeSets(null, 0L,
- null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1);
+
if (!firstChangeSets.getAclChangeSets().isEmpty())
{
AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0);
@@ -425,49 +431,38 @@ public class AclTracker extends ActivatableTracker
}
}
- if (!state.isCheckedFirstAclTransactionTime())
+ if (!state.isCheckedFirstAclTransactionTime() && !firstChangeSets.getAclChangeSets().isEmpty())
{
- firstChangeSets = client.getAclChangeSets(null, 0L,
- null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1);
- if (!firstChangeSets.getAclChangeSets().isEmpty())
+ AclChangeSet firstAclChangeSet = firstChangeSets.getAclChangeSets().get(0);
+ long firstAclTxId = firstAclChangeSet.getId();
+ long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs();
+ int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId),
+ Long.toString(firstAclTxCommitTime));
+
+ if (setSize == 0)
{
- AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0);
- long firstAclTxId = firstAclChangeSet.getId();
- long firstAclTxCommitTime = firstAclChangeSet.getCommitTimeMs();
- int setSize = this.infoSrv.getAclTxDocsSize(Long.toString(firstAclTxId),
- Long.toString(firstAclTxCommitTime));
-
- if (setSize == 0)
- {
- LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName);
- LOGGER.error("SOLR has successfully connected to your repository " +
- "however the SOLR indexes and repository database do not match.");
- LOGGER.error("If this is a new or rebuilt database your SOLR indexes " +
- "also need to be re-built to match the database.");
- LOGGER.error("You can also check your SOLR connection details in solrcore.properties.");
- throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp");
- }
- else if (setSize == 1)
- {
- state.setCheckedFirstTransactionTime(true);
- LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName);
- }
- else
- {
- LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName);
- }
+ LOGGER.error("[CORE {}] First acl transaction was not found with the correct timestamp.", coreName);
+ LOGGER.error("SOLR has successfully connected to your repository " +
+ "however the SOLR indexes and repository database do not match.");
+ LOGGER.error("If this is a new or rebuilt database your SOLR indexes " +
+ "also need to be re-built to match the database.");
+ LOGGER.error("You can also check your SOLR connection details in solrcore.properties.");
+ throw new AlfrescoRuntimeException("Initial acl transaction not found with correct timestamp");
+ }
+ else if (setSize == 1)
+ {
+ state.setCheckedFirstAclTransactionTime(true);
+ LOGGER.info("[CORE {}] Verified first acl transaction and timestamp in index", coreName);
+ }
+ else
+ {
+ LOGGER.warn("[CORE {}] Duplicate initial acl transaction found with correct timestamp", coreName);
}
}
// Checks that the last aclTxId in solr is <= last aclTxId in repo
if (!state.isCheckedLastAclTransactionTime())
{
- if (firstChangeSets == null)
- {
- firstChangeSets = client.getAclChangeSets(null, 0L,
- null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1);
- }
-
Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime();
Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId();
diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclTrackerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclTrackerTest.java
new file mode 100644
index 000000000..1bec433b6
--- /dev/null
+++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/tracker/AclTrackerTest.java
@@ -0,0 +1,153 @@
+/*
+ * #%L
+ * Alfresco Search Services
+ * %%
+ * Copyright (C) 2005 - 2020 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.solr.tracker;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+
+import static org.alfresco.solr.tracker.AclTracker.INITIAL_MAX_ACL_CHANGE_SET_ID;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.openMocks;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.solr.InformationServer;
+import org.alfresco.solr.TrackerState;
+import org.alfresco.solr.client.AclChangeSet;
+import org.alfresco.solr.client.AclChangeSets;
+import org.alfresco.solr.client.SOLRAPIClient;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+/** Unit tests for the {@link AclTracker}. */
+public class AclTrackerTest
+{
+ @InjectMocks
+ private AclTracker aclTracker = new AclTracker();
+ @Mock
+ private SOLRAPIClient solrAPIClient;
+ @Mock
+ private InformationServer informationServer;
+ @Mock
+ private TrackerState trackerState;
+
+ @Before
+ public void setUp()
+ {
+ openMocks(this);
+ }
+
+ /** Check that during the first run (with an empty index) then verification is successful. */
+ @Test
+ public void testCheckRepoAndIndexConsistency_firstRun_success() throws Exception
+ {
+ AclChangeSets firstChangeSets = new AclChangeSets(emptyList());
+ when(solrAPIClient.getAclChangeSets(null, 0L,
+ null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1)).thenReturn(firstChangeSets);
+ when(informationServer.getAclTxDocsSize("1", "1000")).thenReturn(1);
+
+ // Call the method under test.
+ aclTracker.checkRepoAndIndexConsistency(trackerState);
+
+ verify(trackerState).setCheckedFirstAclTransactionTime(true);
+ verify(trackerState).setCheckedLastAclTransactionTime(true);
+ }
+
+ /** Check that subsequent checks of a running index don't make expensive requests. */
+ @Test
+ public void testCheckRepoAndIndexConsistency_alreadyInitialised_success() throws Exception
+ {
+ when(trackerState.getLastGoodChangeSetCommitTimeInIndex()).thenReturn(8000L);
+ when(trackerState.isCheckedFirstAclTransactionTime()).thenReturn(true);
+ when(trackerState.isCheckedLastAclTransactionTime()).thenReturn(true);
+
+ // Call the method under test.
+ aclTracker.checkRepoAndIndexConsistency(trackerState);
+
+ // Check that we don't make any expensive calls to the index or the repo.
+ verifyNoInteractions(solrAPIClient, informationServer);
+ }
+
+ /** Check that after downtime the validation is successful. */
+ @Test
+ public void testCheckRepoAndIndexConsistency_afterRestart_success() throws Exception
+ {
+ when(trackerState.getLastGoodChangeSetCommitTimeInIndex()).thenReturn(8000L);
+ AclChangeSets firstChangeSets = new AclChangeSets(asList(new AclChangeSet(1, 1000, 2)), 8000L, 8L);
+ when(solrAPIClient.getAclChangeSets(null, 0L,
+ null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1)).thenReturn(firstChangeSets);
+ when(informationServer.getAclTxDocsSize("1", "1000")).thenReturn(1);
+
+ // The index is behind the repo.
+ AclChangeSet lastIndexedChangeSet = new AclChangeSet(7, 7000, 7);
+ when(informationServer.getMaxAclChangeSetIdAndCommitTimeInIndex()).thenReturn(lastIndexedChangeSet);
+
+ // Call the method under test.
+ aclTracker.checkRepoAndIndexConsistency(trackerState);
+
+ verify(trackerState).setCheckedFirstAclTransactionTime(true);
+ verify(trackerState).setCheckedLastAclTransactionTime(true);
+ }
+
+ /** Check that if the index is populated but the repository is empty then we get an exception. */
+ @Test(expected = AlfrescoRuntimeException.class)
+ public void testCheckRepoAndIndexConsistency_populatedIndexEmptyRepo_runtimeException() throws Exception
+ {
+ when(trackerState.getLastGoodChangeSetCommitTimeInIndex()).thenReturn(8000L);
+ AclChangeSets firstChangeSets = new AclChangeSets(asList(new AclChangeSet(1, 1000, 2)), 8000L, 8L);
+ when(solrAPIClient.getAclChangeSets(null, 0L,
+ null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1)).thenReturn(firstChangeSets);
+ // The first ACL transaction was not found in the repository.
+ when(informationServer.getAclTxDocsSize("1", "1000")).thenReturn(0);
+
+ AclChangeSet lastIndexedChangeSet = new AclChangeSet(8, 8000, 8);
+ when(informationServer.getMaxAclChangeSetIdAndCommitTimeInIndex()).thenReturn(lastIndexedChangeSet);
+
+ // Call the method under test.
+ aclTracker.checkRepoAndIndexConsistency(trackerState);
+ }
+
+ /** Check that if the last ACL in the index is after the last ACL in the repository then we get an exception. */
+ @Test (expected = AlfrescoRuntimeException.class)
+ public void testCheckRepoAndIndexConsistency_indexAheadOfRepo_runtimeException() throws Exception
+ {
+ when(trackerState.getLastGoodChangeSetCommitTimeInIndex()).thenReturn(8000L);
+ AclChangeSets firstChangeSets = new AclChangeSets(asList(new AclChangeSet(1, 1000, 2)), 8000L, 8L);
+ when(solrAPIClient.getAclChangeSets(null, 0L,
+ null, INITIAL_MAX_ACL_CHANGE_SET_ID, 1)).thenReturn(firstChangeSets);
+ when(informationServer.getAclTxDocsSize("1", "1000")).thenReturn(1);
+
+ // The index contains an ACL after the last one from the server (id 8 at time 8000L).
+ AclChangeSet lastIndexedChangeSet = new AclChangeSet(9, 9000, 9);
+ when(informationServer.getMaxAclChangeSetIdAndCommitTimeInIndex()).thenReturn(lastIndexedChangeSet);
+
+ // Call the method under test.
+ aclTracker.checkRepoAndIndexConsistency(trackerState);
+ }
+}
diff --git a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java
index 052064051..3af03c36f 100644
--- a/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java
+++ b/search-services/alfresco-solrclient-lib/src/main/java/org/alfresco/solr/client/AclChangeSets.java
@@ -43,7 +43,7 @@ public class AclChangeSets
private Long maxChangeSetId;
- AclChangeSets(List aclChangeSets, Long maxChangeSetCommitTime, Long maxChangeSetId)
+ public AclChangeSets(List aclChangeSets, Long maxChangeSetCommitTime, Long maxChangeSetId)
{
this.aclChangeSets = (aclChangeSets == null ? null : new ArrayList<>(aclChangeSets));
this.maxChangeSetCommitTime = maxChangeSetCommitTime;