mirror of
https://github.com/Alfresco/SearchServices.git
synced 2026-09-16 18:12:56 +00:00
Merge branch 'feature/SEARCH_2222_merge_master_in_2_0' into 'release/V2.0.x'
Feature/search 2222 merge master in 2 0 See merge request search_discovery/insightengine!490
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@
|
||||
<name>Search Analytics E2E Tests</name>
|
||||
<description>Test Project to test Search Service and Analytics Features on a complete setup of Alfresco, Share</description>
|
||||
<properties>
|
||||
<tas.rest.api.version>1.37</tas.rest.api.version>
|
||||
<tas.rest.api.version>1.38</tas.rest.api.version>
|
||||
<tas.cmis.api.version>1.13</tas.cmis.api.version>
|
||||
<tas.utility.version>3.0.20</tas.utility.version>
|
||||
<rm.version>3.3.0.1</rm.version>
|
||||
|
||||
@@ -34,6 +34,8 @@ public class TestGroup
|
||||
public static final String CONFIG_SHARDING_EXPLICIT ="CONFIG_Sharding_EXPLICIT"; // Alfresco search services using sharded environment and explicit routing
|
||||
public static final String CONFIG_SHARDING_DB_ID_RANGE = "CONFIG_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE
|
||||
|
||||
public static final String CONFIG_ENABLED_CASCADE_TRACKER ="Config_Enabled_Cascade_Tracker"; // Alfresco search services does not index fields related to cascaded updates
|
||||
|
||||
public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running
|
||||
|
||||
public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<type>d:text</type>
|
||||
<mandatory>false</mandatory>
|
||||
<index enabled="true">
|
||||
<tokenised>TRUE</tokenised>
|
||||
<tokenised>BOTH</tokenised>
|
||||
<facetable>false</facetable>
|
||||
</index>
|
||||
</property>
|
||||
|
||||
+13
@@ -1,5 +1,6 @@
|
||||
package org.alfresco.test.search.functional.searchServices.cmis;
|
||||
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.CustomObjectTypeProperties;
|
||||
import org.alfresco.utility.data.provider.XMLDataConfig;
|
||||
@@ -139,4 +140,16 @@ public class SolrSearchByAspectTests extends AbstractCmisE2ETest
|
||||
cmisApi.authenticateUser(testUser);
|
||||
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), String.format("Result count not as expected for query: %s", currentQuery));
|
||||
}
|
||||
|
||||
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData", groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@XMLDataConfig(file = "src/test/resources/testdata/search-by-aspect-in-tree.xml")
|
||||
public void executeSearchByAspectInTree(QueryModel query) throws Exception
|
||||
{
|
||||
String currentQuery = query.getValue()
|
||||
.replace("NODE_REF[f1]", tasFolder1.getNodeRef())
|
||||
.replace("NODE_REF[s1]", siteDoclibNodeRef);
|
||||
|
||||
cmisApi.authenticateUser(testUser);
|
||||
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), String.format("Result count not as expected for query: %s", currentQuery));
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -1,5 +1,6 @@
|
||||
package org.alfresco.test.search.functional.searchServices.cmis;
|
||||
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.CustomObjectTypeProperties;
|
||||
import org.alfresco.utility.data.provider.XMLDataConfig;
|
||||
@@ -100,4 +101,19 @@ public class SolrSearchByIdTests extends AbstractCmisE2ETest
|
||||
cmisApi.authenticateUser(testUser);
|
||||
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), String.format("Result count not as expected for query: %s", currentQuery));
|
||||
}
|
||||
|
||||
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData", groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@XMLDataConfig(file = "src/test/resources/testdata/search-by-id-in-tree.xml")
|
||||
public void executeSearchByIdInTree(QueryModel query) throws Exception
|
||||
{
|
||||
String currentQuery = query.getValue()
|
||||
.replace("NODE_REF[siteId]", siteDoclibNodeRef)
|
||||
.replace("NODE_REF[d1]", tasSubFile1.getNodeRefWithoutVersion())
|
||||
.replace("NODE_REF[d2]", tasSubFile2.getNodeRefWithoutVersion())
|
||||
.replace("NODE_REF[f1]", tasFolder1.getNodeRef())
|
||||
.replace("NODE_REF[f1-1]", tasSubFolder1.getNodeRef());
|
||||
|
||||
cmisApi.authenticateUser(testUser);
|
||||
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), String.format("Result count not as expected for query: %s", currentQuery));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package org.alfresco.test.search.functional.searchServices.cmis;
|
||||
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.provider.XMLDataConfig;
|
||||
import org.alfresco.utility.data.provider.XMLTestData;
|
||||
@@ -48,7 +49,7 @@ public class SolrSearchByPathTests extends AbstractCmisE2ETest
|
||||
Utility.waitToLoopTime(getSolrWaitTimeInSeconds());
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "prepareDataForSearchByPath", dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData")
|
||||
@Test(dependsOnMethods = "prepareDataForSearchByPath", dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData", groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@XMLDataConfig(file = "src/test/resources/testdata/search-by-path.xml")
|
||||
public void executeSearchByPathQueries(QueryModel query)
|
||||
{
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package org.alfresco.test.search.functional.searchServices.cmis;
|
||||
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.provider.XMLDataConfig;
|
||||
import org.alfresco.utility.data.provider.XMLTestDataProvider;
|
||||
@@ -55,7 +56,7 @@ public class SolrSearchInTreeTests extends AbstractCmisE2ETest
|
||||
dataContent.deleteSite(testSite);
|
||||
}
|
||||
|
||||
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData")
|
||||
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData", groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@XMLDataConfig(file = "src/test/resources/testdata/search-in-tree.xml")
|
||||
public void executeCMISQuery(QueryModel query) throws Exception
|
||||
{
|
||||
|
||||
+5
-3
@@ -31,8 +31,10 @@ import org.alfresco.rest.search.RestRequestQueryModel;
|
||||
import org.alfresco.rest.search.RestResultBucketsModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.rest.search.SearchResponse;
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.test.search.functional.AbstractE2EFunctionalTest;
|
||||
import org.alfresco.utility.data.RandomData;
|
||||
import org.alfresco.utility.data.provider.XMLTestDataProvider;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
@@ -93,7 +95,7 @@ public class FacetFieldsSearchTest extends AbstractE2EFunctionalTest
|
||||
waitForContentIndexing(htmlFile.getContent(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testSearchFacetFieldsBucketExcludedWhenMinCount2() throws Exception
|
||||
{
|
||||
// Create Query with FacetFields: Site and Content MimeType
|
||||
@@ -129,7 +131,7 @@ public class FacetFieldsSearchTest extends AbstractE2EFunctionalTest
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testSearchWithFacetFieldsMinCountChecks() throws Exception
|
||||
{
|
||||
SearchRequest query = new SearchRequest();
|
||||
@@ -204,7 +206,7 @@ public class FacetFieldsSearchTest extends AbstractE2EFunctionalTest
|
||||
Assert.assertNull(response.getContext().getFacetsFields());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testSearchWithFacetFieldsOnlyFacetsWhereAccess() throws Exception
|
||||
{
|
||||
SearchRequest query = new SearchRequest();
|
||||
|
||||
+5
-5
@@ -109,8 +109,8 @@ public class FacetRangeSearchTest extends AbstractSearchServicesE2ETest
|
||||
.containsSummary(String.format(RestErrorModel.MANDATORY_PARAM, "gap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH }, executionType = ExecutionType.REGRESSION,
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Check basic facet range search api")
|
||||
@SuppressWarnings("unchecked")
|
||||
public void searchWithRange()
|
||||
@@ -159,8 +159,8 @@ public class FacetRangeSearchTest extends AbstractSearchServicesE2ETest
|
||||
assertEquals(info.get("endInclusive"), "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH }, executionType = ExecutionType.REGRESSION,
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Check date facet intervals search api")
|
||||
@SuppressWarnings("unchecked")
|
||||
public void searchWithRangeHardend()
|
||||
@@ -257,7 +257,7 @@ public class FacetRangeSearchTest extends AbstractSearchServicesE2ETest
|
||||
query.setRanges(ranges);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Check basic facet range search api")
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
+2
-2
@@ -102,8 +102,8 @@ public class FacetedSearchTest extends AbstractSearchServicesE2ETest
|
||||
waitForContentIndexing(file4.getContent(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestRail(section = { TestGroup.REST_API, TestGroup.SEARCH }, executionType = ExecutionType.REGRESSION, description = "Checks facet queries for the Search api")
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = { TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION, description = "Checks facet queries for the Search api")
|
||||
public void searchWithQueryFaceting() throws Exception
|
||||
{
|
||||
SearchRequest query = new SearchRequest();
|
||||
|
||||
+132
-76
@@ -18,9 +18,11 @@
|
||||
*/
|
||||
package org.alfresco.test.search.functional.searchServices.search;
|
||||
|
||||
import static jersey.repackaged.com.google.common.collect.Sets.newHashSet;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import org.alfresco.rest.search.SearchNodeModel;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.rest.search.SearchResponse;
|
||||
import org.alfresco.test.search.functional.AbstractE2EFunctionalTest;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
@@ -35,12 +37,18 @@ import org.testng.annotations.Test;
|
||||
* Search end point Public API test with finger print.
|
||||
*
|
||||
* @author Michael Suzuki
|
||||
* @author Meenal Bhave
|
||||
*/
|
||||
public class FingerPrintTest extends AbstractE2EFunctionalTest
|
||||
{
|
||||
private FileModel file1;
|
||||
private FileModel file2;
|
||||
private FolderModel folder;
|
||||
private FileModel fileBanana, fileTaco, fileCat, fileDog, fileOriginal;
|
||||
/**
|
||||
* File to be created in dataprep with same content as fileOriginal
|
||||
* File to be updated in test searchAfterVersionUpdate
|
||||
* File to be reverted in test searchAfterVersionRevert
|
||||
*/
|
||||
private FileModel fileToBeUpdated;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws Exception
|
||||
@@ -51,30 +59,39 @@ public class FingerPrintTest extends AbstractE2EFunctionalTest
|
||||
* |-- pangram-banana.txt
|
||||
* |-- pangram-taco.txt
|
||||
* |-- pangram-cat.txt
|
||||
* |-- dog.txt
|
||||
* |-- pangram-dog.txt
|
||||
* |-- original.txt
|
||||
* |-- toBeUpdated.txt
|
||||
*/
|
||||
folder = new FolderModel("The quick brown fox jumps over");
|
||||
dataContent.usingUser(testUser).usingSite(testSite).createFolder(folder);
|
||||
|
||||
file1 = new FileModel("pangram-banana.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy banana");
|
||||
file2 = new FileModel("pangram-taco.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy dog that ate the taco");
|
||||
fileBanana = new FileModel("pangram-banana.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy banana");
|
||||
fileTaco = new FileModel("pangram-taco.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy dog that ate the taco");
|
||||
|
||||
FileModel file3 = new FileModel("pangram-cat.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy cat");
|
||||
FileModel file4 = new FileModel("dog.txt", FileType.TEXT_PLAIN, "The quick brown fox ate the lazy dog");
|
||||
fileCat = new FileModel("pangram-cat.txt", FileType.TEXT_PLAIN, "The quick brown fox jumps over the lazy cat");
|
||||
fileDog = new FileModel("pangram-dog.txt", FileType.TEXT_PLAIN, "The quick brown fox ate the lazy dog");
|
||||
|
||||
// fileOriginal with different content
|
||||
fileOriginal = new FileModel("original.txt", FileType.TEXT_PLAIN, "This is an original file with some content");
|
||||
|
||||
// Index a new fileToBeUpdated with content similar to fileOriginal
|
||||
fileToBeUpdated = new FileModel("toBeUpdated.txt", FileType.TEXT_PLAIN, "This is an original file with some content");
|
||||
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(file1);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(file2);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(file3);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(file4);
|
||||
|
||||
waitForContentIndexing(file4.getContent(), true);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileBanana);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileTaco);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileCat);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileDog);
|
||||
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileOriginal);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(folder).createContent(fileToBeUpdated);
|
||||
|
||||
// Additional wait implemented to remove inconsistent failures. Ref: Search-1438 for details
|
||||
waitForIndexing("FINGERPRINT:" + file2.getNodeRefWithoutVersion(), true);
|
||||
waitForIndexing("FINGERPRINT:" + file3.getNodeRefWithoutVersion(), true);
|
||||
waitForIndexing("FINGERPRINT:" + fileDog.getNodeRefWithoutVersion(), true);
|
||||
waitForIndexing("FINGERPRINT:" + fileToBeUpdated.getNodeRefWithoutVersion(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(priority = 1)
|
||||
@Bug(id = "MNT-20449")
|
||||
public void makeSureFingerprintQueryWorksAfterMetadataUpdate() throws Exception
|
||||
{
|
||||
@@ -105,86 +122,125 @@ public class FingerPrintTest extends AbstractE2EFunctionalTest
|
||||
* to the files loaded as part of this test.
|
||||
* Note that for fingerprint to work it need a 5 word sequence.
|
||||
*/
|
||||
@Test
|
||||
@Test(priority = 2)
|
||||
public void search()
|
||||
{
|
||||
String uuid = file1.getNodeRefWithoutVersion();
|
||||
String uuid = fileBanana.getNodeRefWithoutVersion();
|
||||
Assert.assertNotNull(uuid);
|
||||
String fingerprint = String.format("FINGERPRINT:%s", uuid);
|
||||
SearchResponse response = query(fingerprint);
|
||||
|
||||
String fingerprintQuery = String.format("FINGERPRINT:%s", uuid);
|
||||
SearchResponse response = queryAsUser(testUser, fingerprintQuery);
|
||||
|
||||
int count = response.getEntries().size();
|
||||
assertTrue(count > 1);
|
||||
for (SearchNodeModel m : response.getEntries())
|
||||
{
|
||||
String match = m.getModel().getName();
|
||||
switch (match)
|
||||
{
|
||||
case "pangram.txt":
|
||||
case "pangram-banana.txt":
|
||||
case "pangram-taco.txt":
|
||||
case "pangram-cat.txt":
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Not a match to an expected file: " + m.getModel().getName());
|
||||
}
|
||||
m.getModel().assertThat().field("name").isNot("dog.txt");
|
||||
m.getModel().assertThat().field("name").isNot("cars.txt");
|
||||
}
|
||||
|
||||
Set<String> expectedNames = newHashSet();
|
||||
expectedNames.add(fileBanana.getName());
|
||||
expectedNames.add(fileTaco.getName());
|
||||
expectedNames.add(fileCat.getName());
|
||||
|
||||
testSearchQueryUnordered(fingerprintQuery, expectedNames, SearchLanguage.AFTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(priority = 3)
|
||||
public void searchSimilar()
|
||||
{
|
||||
String uuid = file2.getNodeRefWithoutVersion();
|
||||
String uuid = fileTaco.getNodeRefWithoutVersion();
|
||||
Assert.assertNotNull(uuid);
|
||||
|
||||
// In the response entity there is a score of each doc, change below threshold to bring more like or less.
|
||||
String fingerprint = String.format("FINGERPRINT:%s_68", uuid);
|
||||
SearchResponse response = query(fingerprint);
|
||||
String fingerprintQuery = String.format("FINGERPRINT:%s_68", uuid);
|
||||
SearchResponse response = query(fingerprintQuery);
|
||||
|
||||
int count = response.getEntries().size();
|
||||
assertTrue(count > 1);
|
||||
|
||||
for (SearchNodeModel m : response.getEntries())
|
||||
{
|
||||
switch (m.getModel().getName())
|
||||
{
|
||||
case "pangram.txt":
|
||||
case "pangram-taco.txt":
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Not a match to an expected file: " + m.getModel().getName());
|
||||
}
|
||||
m.getModel().assertThat().field("name").isNot("pangram-banana.txt");
|
||||
m.getModel().assertThat().field("name").isNot("pangram-cat.txt");
|
||||
m.getModel().assertThat().field("name").isNot("dog.txt");
|
||||
m.getModel().assertThat().field("name").isNot("cars.txt");
|
||||
}
|
||||
|
||||
Set<String> expectedNames = newHashSet();
|
||||
expectedNames.add(fileTaco.getName());
|
||||
|
||||
testSearchQueryUnordered(fingerprintQuery, expectedNames, SearchLanguage.AFTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(priority = 4)
|
||||
public void searchSimilar67Percent()
|
||||
{
|
||||
String uuid = file2.getNodeRefWithoutVersion();
|
||||
String uuid = fileTaco.getNodeRefWithoutVersion();
|
||||
Assert.assertNotNull(uuid);
|
||||
String fingerprint = String.format("FINGERPRINT:%s_68", uuid);
|
||||
SearchResponse response = query(fingerprint);
|
||||
|
||||
// Check that: When asked for less overlap of 67%: More documents are matched
|
||||
String fingerprintQuery = String.format("FINGERPRINT:%s_67", uuid);
|
||||
SearchResponse response = query(fingerprintQuery);
|
||||
|
||||
int count = response.getEntries().size();
|
||||
assertTrue(count > 1);
|
||||
for (SearchNodeModel m : response.getEntries())
|
||||
{
|
||||
switch (m.getModel().getName())
|
||||
{
|
||||
case "pangram.txt":
|
||||
case "pangram-taco.txt":
|
||||
case "pangram-cat.txt":
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Not a match to an expected file: " + m.getModel().getName());
|
||||
}
|
||||
m.getModel().assertThat().field("name").isNot("pangram-banana.txt");
|
||||
m.getModel().assertThat().field("name").isNot("dog.txt");
|
||||
m.getModel().assertThat().field("name").isNot("cars.txt");
|
||||
}
|
||||
|
||||
Set<String> expectedNames = newHashSet();
|
||||
expectedNames.add(fileTaco.getName());
|
||||
expectedNames.add(fileBanana.getName());
|
||||
expectedNames.add(fileCat.getName());
|
||||
|
||||
testSearchQueryUnordered(fingerprintQuery, expectedNames, SearchLanguage.AFTS);
|
||||
}
|
||||
|
||||
@Test(priority = 5, enabled = false)
|
||||
@Bug(id = "SEARCH-2065")
|
||||
public void searchAfterVersionUpdate()
|
||||
{
|
||||
// Check that fileToBeUpdated is found with a fingerprint query with fileOriginal: as they have similar content
|
||||
boolean found = isContentInSearchResults("FINGERPRINT:" + fileOriginal.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), true);
|
||||
Assert.assertTrue(found, "Matching File Not found in results for Fingerprint Query with original file");
|
||||
|
||||
// Update content of the fileToBeUpdated to match file1 in the dataprep
|
||||
String newFileContent = "The quick brown fox jumps over the updated file";
|
||||
fileToBeUpdated.setContent(newFileContent);
|
||||
dataContent.usingUser(testUser).usingSite(testSite).usingResource(fileToBeUpdated).updateContent(newFileContent);
|
||||
|
||||
// Wair for the new version of the file to be indexed
|
||||
assertTrue(waitForContentIndexing(fileToBeUpdated.getContent(), true));
|
||||
|
||||
// Check that fileToBeUpdated is NOT found with a fingerprint query with fileOriginal
|
||||
boolean notFound = isContentInSearchResults("FINGERPRINT:" + fileOriginal.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), false);
|
||||
Assert.assertTrue(notFound, "Updated File unexpectedly found in results for Fingerprint Query with original file");
|
||||
|
||||
// Check that fileToBeUpdated is found with a fingerprint query with file1
|
||||
found = isContentInSearchResults("FINGERPRINT:" + fileBanana.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), true);
|
||||
Assert.assertTrue(found, "Update File Not found in results for Fingerprint Query with updated content");
|
||||
}
|
||||
|
||||
@Test(priority = 6, enabled = false)
|
||||
@Bug(id = "SEARCH-2065")
|
||||
public void searchAfterVersionRevert() throws Exception
|
||||
{
|
||||
// Revert fileToBeUpdated to previous version
|
||||
restClient.authenticateUser(testUser).withCoreAPI().usingNode(fileToBeUpdated).revertVersion("1.0", "{}");
|
||||
String revertedContent = restClient.authenticateUser(testUser).withCoreAPI().usingNode(fileToBeUpdated).getVersionContent("1.2").toString();
|
||||
Assert.assertEquals(revertedContent, fileOriginal.getContent(), "Reverted content does not match Original");
|
||||
|
||||
// Wair for the new version of the file to be indexed
|
||||
assertTrue(waitForContentIndexing(fileToBeUpdated.getContent(), true));
|
||||
|
||||
// Check that fileToBeUpdated is found with a fingerprint query with fileOriginal
|
||||
boolean found = isContentInSearchResults("FINGERPRINT:" + fileOriginal.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), true);
|
||||
Assert.assertTrue(found, "File not found in results for Fingerprint Query with original file after reverting version changes");
|
||||
|
||||
// Check that fileToBeUpdated is NOT found with a fingerprint query with file1
|
||||
boolean notFound = isContentInSearchResults("FINGERPRINT:" + fileBanana.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), false);
|
||||
Assert.assertTrue(notFound, "File appears in the results for Fingerprint Query even after reverting content changes");
|
||||
}
|
||||
|
||||
@Test(priority = 7, enabled = false)
|
||||
@Bug(id = "SEARCH-2065")
|
||||
public void searchAfterVersionDelete() throws Exception
|
||||
{
|
||||
// Revert fileToBeUpdated to previous version
|
||||
restClient.authenticateUser(testUser).withCoreAPI().usingNode(fileToBeUpdated).deleteNodeVersion("1.2");
|
||||
|
||||
// Check that fileToBeUpdated is NOT found with a fingerprint query with fileOriginal
|
||||
boolean notFound = isContentInSearchResults("FINGERPRINT:" + fileOriginal.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), false);
|
||||
Assert.assertTrue(notFound, "Updated File unexpectedly found in results for Fingerprint Query with original file");
|
||||
|
||||
// Check that fileToBeUpdated is found with a fingerprint query with file1
|
||||
boolean found = isContentInSearchResults("FINGERPRINT:" + fileBanana.getNodeRefWithoutVersion(), fileToBeUpdated.getName(), true);
|
||||
Assert.assertTrue(found, "Update File Not found in results for Fingerprint Query with updated content");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -119,8 +119,8 @@ public class PivotFacetedSearchTest extends AbstractSearchServicesE2ETest
|
||||
assertPivotResponse(response, "creator", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH },
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH},
|
||||
executionType = ExecutionType.REGRESSION,
|
||||
description = "Checks nested pivot using Search api")
|
||||
public void searchWithNestedPivoting()
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ public class SearchAFTSInFieldTest extends AbstractE2EFunctionalTest
|
||||
});
|
||||
}
|
||||
|
||||
@Test(priority = 4)
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testSearchInFieldSITE()
|
||||
{
|
||||
String query = "SITE:" + testSite.getId();
|
||||
|
||||
+4
-3
@@ -29,6 +29,7 @@ import org.alfresco.rest.search.RestRequestQueryModel;
|
||||
import org.alfresco.rest.search.RestResultBucketsModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.rest.search.SearchResponse;
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
@@ -92,7 +93,7 @@ public class SearchAPATHTest extends AbstractSearchServicesE2ETest
|
||||
*}}
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void searchLevel0()
|
||||
{
|
||||
SearchRequest searchQuery = searchRequestWithAPATHFacet("name:*", "0");
|
||||
@@ -108,7 +109,7 @@ public class SearchAPATHTest extends AbstractSearchServicesE2ETest
|
||||
* Test to test that the facet buckets are returned correctly for sub-level 1/
|
||||
* Test to search for a searchString, that's unique to the test run and hence stable for any environment
|
||||
*/
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void searchLevel0andIncludeSubLevel1()
|
||||
{
|
||||
SearchRequest searchQuery = searchRequestWithAPATHFacet("name:" + unique_searchString, "1/");
|
||||
@@ -120,7 +121,7 @@ public class SearchAPATHTest extends AbstractSearchServicesE2ETest
|
||||
getFirstBucket(response).assertThat().field("label").contains("1/");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void searchLevel2()
|
||||
{
|
||||
String queryString = "name:cars";
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ package org.alfresco.test.search.functional.searchServices.search;
|
||||
|
||||
import org.alfresco.rest.model.RestNodeAssociationModelCollection;
|
||||
import org.alfresco.rest.model.RestNodeChildAssociationModel;
|
||||
import org.alfresco.search.TestGroup;
|
||||
import org.alfresco.test.search.functional.AbstractE2EFunctionalTest;
|
||||
import org.alfresco.utility.data.CustomObjectTypeProperties;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
@@ -52,7 +53,7 @@ public class SearchSecondaryAssociationTest extends AbstractE2EFunctionalTest
|
||||
waitForMetadataIndexing(file1.getName(), true);
|
||||
}
|
||||
|
||||
@Test(priority = 1)
|
||||
@Test(priority = 1, groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testSearchPathForSecondaryAssociation() throws Exception
|
||||
{
|
||||
String queryPathFolder1 = "PATH:\"/app:company_home/st:sites/cm:" + testSite.getTitle() +
|
||||
|
||||
+4
-4
@@ -101,8 +101,8 @@ public class SearchTest extends AbstractSearchServicesE2ETest
|
||||
response.getContext().assertThat().field("request").isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH }, executionType = ExecutionType.REGRESSION,
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Tests a search request containing a sort clause.")
|
||||
public void searchWithOneSortClause()
|
||||
{
|
||||
@@ -148,8 +148,8 @@ public class SearchTest extends AbstractSearchServicesE2ETest
|
||||
* The first clause has always the same value for all matches so the test makes sure the request is correctly
|
||||
* processed and the returned order is determined by the second clause.
|
||||
*/
|
||||
@Test
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH }, executionType = ExecutionType.REGRESSION,
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Tests a search request containing a sort clause.")
|
||||
public void searchWithTwoSortClauses()
|
||||
{
|
||||
|
||||
+1
-1
@@ -203,7 +203,7 @@ public class StatsSearchTest extends AbstractSearchServicesE2ETest
|
||||
assertTrue((Integer)metricCount.get("countValues") > count, "With the exclude filter there will be more documents than returned");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.SEARCH},
|
||||
executionType = ExecutionType.REGRESSION,
|
||||
description = "Checks errors with stats with Pivot using Search api")
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ public class CascadingIntegrationTest extends AbstractE2EFunctionalTest
|
||||
"failing while removing sharding model");
|
||||
}
|
||||
|
||||
@Test(priority = 1)
|
||||
@Test(priority = 1, groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testChildPathWhenParentRenamed() throws Exception
|
||||
{
|
||||
// Create Parent folder
|
||||
@@ -119,7 +119,7 @@ public class CascadingIntegrationTest extends AbstractE2EFunctionalTest
|
||||
Assert.assertEquals(descendantCountOfOriginalName, 0, "Old path still has descendants: " + parentQuery);
|
||||
}
|
||||
|
||||
@Test(priority = 2)
|
||||
@Test(priority = 2, groups={TestGroup.CONFIG_ENABLED_CASCADE_TRACKER})
|
||||
public void testGrandChildPathWhenGrandParentRenamed() throws Exception
|
||||
{
|
||||
// Create grand parent folder
|
||||
|
||||
+19
-6
@@ -475,17 +475,24 @@ public class SolrE2eAdminTest extends AbstractE2EFunctionalTest
|
||||
|
||||
checkResponseStatusOk(response);
|
||||
|
||||
Integer expand = response.getResponse().body().jsonPath().get("expand");
|
||||
Integer expand = response.getResponse().body().jsonPath().get("expand");
|
||||
|
||||
// RangeCheck action only applies to DB_ID_RANGE Sharding method, so expect error in other sharding methods and success for DB_ID_RANGE
|
||||
if (ShardingMethod.DB_ID_RANGE.toString().equalsIgnoreCase(getShardMethod()))
|
||||
{
|
||||
// This assertion replicates: testRangeCheckSharding, priority = 21, hence deleting that test as duplicate
|
||||
Assert.assertNotEquals(expand, Integer.valueOf(-1), "Expansion is not successful when not using Shard DB_ID_RANGE method,");
|
||||
// Value -1 is expected when the next shard already has nodes indexed.
|
||||
Assert.assertTrue((expand == Integer.valueOf(-1) || expand == Integer.valueOf(0)), "RangeCheck should not have been allowed when not using Shard DB_ID_RANGE method,");
|
||||
|
||||
Integer minDbid = response.getResponse().body().jsonPath().get("minDbid");
|
||||
Assert.assertTrue(minDbid > 0, "RangeCheck is not successful when not using Shard DB_ID_RANGE method,");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertEquals(expand, Integer.valueOf(-1), "Expansion should not have been allowed when not using Shard DB_ID_RANGE method,");
|
||||
Assert.assertEquals(expand, Integer.valueOf(-1), "RangeCheck should not have been allowed when not using Shard DB_ID_RANGE method,");
|
||||
|
||||
String exception = response.getResponse().body().jsonPath().get("exception");
|
||||
Assert.assertEquals(exception, "ERROR: Wrong document router type:DBIDRouter", "Expansion should not have been allowed when not using Shard DB_ID_RANGE method,");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,12 +516,18 @@ public class SolrE2eAdminTest extends AbstractE2EFunctionalTest
|
||||
// Expand action only applies to DB_ID_RANGE Sharding method, so expect error in other sharding methods and success for DB_ID_RANGE
|
||||
if (ShardingMethod.DB_ID_RANGE.toString().equalsIgnoreCase(getShardMethod()))
|
||||
{
|
||||
// This assertion replicates: testExpandSharding, priority = 23, hence deleting that test as duplicate
|
||||
Assert.assertNotEquals(expand, Integer.valueOf(-1), "Expansion is not successful when not using Shard DB_ID_RANGE method,");
|
||||
// This assertion replicates: testExpandSharding, priority = 23, hence deleting that test as duplicate
|
||||
Assert.assertTrue((expand == Integer.valueOf(-1) || expand == Integer.valueOf(0)), "Expansion is not successful when not using Shard DB_ID_RANGE method,");
|
||||
if (expand == Integer.valueOf(-1))
|
||||
{
|
||||
String exceptionExpected = "Expansion cannot occur if max DBID in the index is more then 75% of range.";
|
||||
String exception = response.getResponse().body().jsonPath().get("exception");
|
||||
Assert.assertEquals(exception, exceptionExpected, "Expansion failed with unexpected Exception while using DB_ID_RANGE sharding");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertEquals(expand, Integer.valueOf(-1), "Expansion should not have been allowed when not using Shard DB_ID_RANGE method,");
|
||||
Assert.assertEquals(expand, Integer.valueOf(-1), "Expansion should not have been allowed when not using Shard DB_ID_RANGE method");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="https://gitlab.alfresco.com/tas/tas-namespaces/raw/master/input-test-data.xsd">
|
||||
<queries>
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[f1]')" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]')" expectedResults="6" />
|
||||
<query value="SELECT * FROM cm:dublincore where IN_TREE('NODE_REF[f1]')" expectedResults="0" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]')" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]')" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:lastModificationDate ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:lastModificationDate DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:createdBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:createdBy ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name DESC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:lastModificationDate ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:createdBy ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:lastModificationDate DESC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:createdBy DESC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="6" />
|
||||
</queries>
|
||||
</testData>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="https://gitlab.alfresco.com/tas/tas-namespaces/raw/master/input-test-data.xsd">
|
||||
|
||||
<queries>
|
||||
<query value="SELECT * FROM tas:tasContentAspect where tas:TextPropertyAC IS NOT NULL" expectedResults="5" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where tas:TextPropertyAC IS NULL" expectedResults="1" />
|
||||
@@ -58,27 +57,6 @@
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_FOLDER('NODE_REF[f1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_FOLDER('NODE_REF[f1]') ORDER BY cmis:name DESC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_FOLDER('NODE_REF[s1]')" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[f1]')" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]')" expectedResults="6" />
|
||||
<query value="SELECT * FROM cm:dublincore where IN_TREE('NODE_REF[f1]')" expectedResults="0" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]')" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]')" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:lastModificationDate ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:lastModificationDate DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:createdBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:createdBy ASC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[f1]') ORDER BY cmis:name DESC, cmis:lastModifiedBy DESC" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:lastModificationDate ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:createdBy ASC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasFolderAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:lastModificationDate DESC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:createdBy DESC" expectedResults="6" />
|
||||
<query value="SELECT * FROM tas:tasContentAspect where IN_TREE('NODE_REF[s1]') ORDER BY cmis:name ASC, cmis:lastModifiedBy DESC" expectedResults="6" />
|
||||
<query value="SELECT D.*, O.* FROM cmis:document as D JOIN tas:tasContentAspect AS O ON D.cmis:objectId = O.cmis:objectId" expectedResults="6" />
|
||||
<query value="SELECT D.*, O.* FROM cmis:document as D JOIN tas:tasContentAspect AS O ON D.cmis:objectId = O.cmis:objectId WHERE O.tas:TextPropertyAC ='aspect content text file-2'"
|
||||
expectedResults="1" />
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="https://gitlab.alfresco.com/tas/tas-namespaces/raw/master/input-test-data.xsd">
|
||||
<queries>
|
||||
<query value="SELECT * FROM tas:document WHERE IN_TREE('NODE_REF[siteId]')" expectedResults="4" />
|
||||
</queries>
|
||||
</testData>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<query value="SELECT * FROM tas:document" expectedResults="4" />
|
||||
<query value="SELECT * FROM tas:folder WHERE IN_FOLDER('NODE_REF[siteId]')" expectedResults="2" />
|
||||
<query value="SELECT * FROM tas:document WHERE IN_FOLDER('NODE_REF[siteId]')" expectedResults="1" />
|
||||
<query value="SELECT * FROM tas:document WHERE IN_TREE('NODE_REF[siteId]')" expectedResults="4" />
|
||||
<query value="SELECT * FROM cmis:document WHERE cmis:objectTypeId = 'D:tas:document'" expectedResults="4" />
|
||||
<query value="SELECT * FROM cmis:document WHERE cmis:objectTypeId <> 'D:tas:document' AND cmis:name LIKE 'standard%'" expectedResults="2" />
|
||||
<query value="SELECT * FROM cmis:document WHERE cmis:objectTypeId IN ('D:tas:document') AND cmis:name LIKE 'tas%'" expectedResults="4" />
|
||||
|
||||
+30
-24
@@ -1,24 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
public interface QueueHandler
|
||||
{
|
||||
void removeFromQueueAndProdHead(AbstractWorkerRunnable job);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr;
|
||||
|
||||
/**
|
||||
* Marker exception thrown for indicating a failure in obtaining / releasing a lock.
|
||||
*/
|
||||
public class AlfrescoLockException extends Exception
|
||||
{
|
||||
AlfrescoLockException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.alfresco.solr;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -794,8 +796,7 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
fieldName = getStoredTextField(propertyName);
|
||||
}
|
||||
|
||||
FieldInstance field = new FieldInstance(fieldName, false, false);
|
||||
indexedField.getFields().add(field);
|
||||
indexedField.addField(fieldName, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1436,7 +1437,7 @@ public class AlfrescoSolrDataModel implements QueryConstants
|
||||
|
||||
public List<FieldInstance> getFields()
|
||||
{
|
||||
return fields;
|
||||
return unmodifiableList(fields);
|
||||
}
|
||||
|
||||
public void addField(String prefix, boolean localised, boolean sort)
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ import org.apache.solr.common.params.SolrParams;
|
||||
*/
|
||||
public class HandlerOfResources {
|
||||
|
||||
public static final List<String> DISALLOWED_SHARED_UPDATES = Arrays.asList("alfresco.identifier.property.",
|
||||
protected static final List<String> DISALLOWED_SHARED_UPDATES = Arrays.asList("alfresco.identifier.property.",
|
||||
"alfresco.suggestable.property.",
|
||||
"alfresco.cross.locale.property.",
|
||||
"alfresco.cross.locale.datatype.");
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ public interface InformationServer extends InformationServerCollectionProvider
|
||||
|
||||
IndexHealthReport reportIndexTransactions(Long minTxId, IOpenBitSet txIdsInDb, long maxTxId) throws IOException;
|
||||
|
||||
List<TenantDbId> getDocsWithUncleanContent(int start, int rows) throws IOException;
|
||||
List<TenantDbId> getDocsWithUncleanContent() throws IOException;
|
||||
|
||||
void updateContent(TenantDbId docRef) throws Exception;
|
||||
|
||||
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2013 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* A {@link List} implementation, backed by an array, taht supports resizing.
|
||||
*
|
||||
* This class supports the reuse of arrays across cache instances, reducing the number of
|
||||
* temporary objects created by the Alfresco Solr indexing service.
|
||||
*
|
||||
* @author Alex Miller
|
||||
*/
|
||||
public class ResizeableArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Object[] values;
|
||||
private int size;
|
||||
boolean active = false;
|
||||
|
||||
public ResizeableArrayList()
|
||||
{
|
||||
this(10);
|
||||
}
|
||||
|
||||
public ResizeableArrayList(int initialSize)
|
||||
{
|
||||
values = new Object[initialSize];
|
||||
size = initialSize;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E get(int index)
|
||||
{
|
||||
checkSize(index);
|
||||
return (E)values[index];
|
||||
}
|
||||
|
||||
private void checkSize(int index)
|
||||
{
|
||||
if (index >= size)
|
||||
{
|
||||
throw new ArrayIndexOutOfBoundsException("Index: " + index + ", Size: " + values.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the underlying to at least minSize.
|
||||
*
|
||||
* @param minSize
|
||||
* @throws IllegalStateException if the instance is not active.
|
||||
*/
|
||||
public void resize(int minSize)
|
||||
{
|
||||
isActive();
|
||||
int oldSize = values.length;
|
||||
if (minSize > oldSize)
|
||||
{
|
||||
int newSize = (oldSize * 3)/2 + 1;
|
||||
if (newSize < minSize)
|
||||
{
|
||||
newSize = minSize;
|
||||
}
|
||||
// minCapacity is usually close to size, so this is a win:
|
||||
values = Arrays.copyOf(values, newSize);
|
||||
}
|
||||
size = minSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy elements from from into this instance
|
||||
*/
|
||||
public <T> void copyFrom(ResizeableArrayList<T> from)
|
||||
{
|
||||
isActive();
|
||||
values = Arrays.copyOf(from.values, from.values.length);
|
||||
size = from.size;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E set(int index, E value)
|
||||
{
|
||||
checkSize(index);
|
||||
isActive();
|
||||
|
||||
E oldValue = (E)values[index];
|
||||
values[index] = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected Object clone() throws CloneNotSupportedException
|
||||
{
|
||||
try {
|
||||
ResizeableArrayList<E> v = (ResizeableArrayList<E>) super.clone();
|
||||
v.values = Arrays.copyOf(values, values.length);
|
||||
v.size = size;
|
||||
return v;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
private void isActive()
|
||||
{
|
||||
if (!active)
|
||||
{
|
||||
throw new IllegalStateException("Not active");
|
||||
}
|
||||
}
|
||||
|
||||
void activate()
|
||||
{
|
||||
active = true;
|
||||
}
|
||||
|
||||
void deactivate()
|
||||
{
|
||||
active = false;
|
||||
for (int i = 0 ; i < size ; i++)
|
||||
{
|
||||
values[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the elements, in-place, contained in this list using {@link Comparator#}
|
||||
*
|
||||
* @param comparator The {@link Comparator} to sort with
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void sort(Comparator<? super E> comparator)
|
||||
{
|
||||
DualPivotQuickSort.sort((E[])values, 0, size -1 , comparator);
|
||||
}
|
||||
}
|
||||
+52
-31
@@ -20,6 +20,7 @@ package org.alfresco.solr;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_ACLID;
|
||||
import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_ACLTXCOMMITTIME;
|
||||
@@ -73,6 +74,9 @@ import static org.alfresco.solr.AlfrescoSolrDataModel.getAclChangeSetDocumentId;
|
||||
import static org.alfresco.solr.AlfrescoSolrDataModel.getAclDocumentId;
|
||||
import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
import static org.alfresco.util.ISO8601DateFormat.isTimeComponentDefined;
|
||||
import static org.alfresco.service.cmr.security.AuthorityType.EVERYONE;
|
||||
import static org.alfresco.service.cmr.security.AuthorityType.GROUP;
|
||||
import static org.alfresco.service.cmr.security.AuthorityType.GUEST;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -821,7 +825,7 @@ public class SolrInformationServer implements InformationServer
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TenantDbId> getDocsWithUncleanContent(int start, int rows) throws IOException
|
||||
public List<TenantDbId> getDocsWithUncleanContent() throws IOException
|
||||
{
|
||||
RefCounted<SolrIndexSearcher> refCounted = null;
|
||||
try
|
||||
@@ -846,7 +850,7 @@ public class SolrInformationServer implements InformationServer
|
||||
* in current snapshot of the index.
|
||||
*
|
||||
* The code below runs every two minutes and purges transactions from the
|
||||
* cleanContentCache that is more then 20 minutes old.
|
||||
* cleanContentCache that is more than 20 minutes old.
|
||||
*
|
||||
*/
|
||||
long purgeTime = System.currentTimeMillis();
|
||||
@@ -880,6 +884,9 @@ public class SolrInformationServer implements InformationServer
|
||||
delegatingCollector.setLastDelegate(collector);
|
||||
searcher.search(documentsWithOutdatedContentQuery(), delegatingCollector);
|
||||
|
||||
LOGGER.debug("{}-[CORE {}] Processing {} documents with content to be indexed", Thread.currentThread().getId(), core.getName(), collector.getTotalHits());
|
||||
|
||||
|
||||
if(collector.getTotalHits() == 0)
|
||||
{
|
||||
LOGGER.debug("No documents with outdated text content have been found.");
|
||||
@@ -1670,11 +1677,11 @@ public class SolrInformationServer implements InformationServer
|
||||
nmdp.setToNodeId(node.getId());
|
||||
nmdp.setMaxResults(Integer.MAX_VALUE);
|
||||
|
||||
Collection<NodeMetaData> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
Optional<Collection<NodeMetaData>> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
|
||||
if (nodeMetaDatas.isEmpty()) return;
|
||||
if (nodeMetaDatas.isEmpty() || nodeMetaDatas.get().isEmpty()) return;
|
||||
|
||||
NodeMetaData nodeMetaData = nodeMetaDatas.iterator().next();
|
||||
NodeMetaData nodeMetaData = nodeMetaDatas.get().iterator().next();
|
||||
if (node.getTxnId() == Long.MAX_VALUE)
|
||||
{
|
||||
LOGGER.debug("Node {} index request is part of a re-index.", node.getId());
|
||||
@@ -1813,8 +1820,8 @@ public class SolrInformationServer implements InformationServer
|
||||
nmdp.setIncludeTxnId(true);
|
||||
nmdp.setMaxResults(1);
|
||||
// Gets only one
|
||||
Collection<NodeMetaData> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
allNodeMetaDatas.addAll(nodeMetaDatas);
|
||||
Optional<Collection<NodeMetaData>> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
allNodeMetaDatas.addAll(nodeMetaDatas.orElse(Collections.emptyList()));
|
||||
}
|
||||
|
||||
return allNodeMetaDatas;
|
||||
@@ -1954,7 +1961,14 @@ public class SolrInformationServer implements InformationServer
|
||||
nmdp.setIncludePaths(false);
|
||||
nmdp.setIncludeParentAssociations(false);
|
||||
nmdp.setMaxResults(Integer.MAX_VALUE);
|
||||
nodeMetaDatas.addAll(getNodesMetaDataFromRepository(nmdp));
|
||||
|
||||
Optional<Collection<NodeMetaData>> nodesMetaDataFromRepository = getNodesMetaDataFromRepository(nmdp);
|
||||
if (nodesMetaDataFromRepository.isEmpty())
|
||||
{
|
||||
// Using exception for flow handling to jump to single node processing.
|
||||
throw new Exception("Error loading node metadata from repository for bulk delete.");
|
||||
}
|
||||
nodeMetaDatas.addAll(nodesMetaDataFromRepository.get());
|
||||
}
|
||||
|
||||
for (NodeMetaData nodeMetaData : nodeMetaDatas)
|
||||
@@ -1988,10 +2002,15 @@ public class SolrInformationServer implements InformationServer
|
||||
|
||||
// Fetches bulk metadata
|
||||
nmdp.setMaxResults(Integer.MAX_VALUE);
|
||||
Collection<NodeMetaData> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
Optional<Collection<NodeMetaData>> nodesMetaDataFromRepository = getNodesMetaDataFromRepository(nmdp);
|
||||
if (nodesMetaDataFromRepository.isEmpty())
|
||||
{
|
||||
// Using exception for flow handling to jump to single node processing.
|
||||
throw new Exception("Error loading node metadata from repository for bulk delete.");
|
||||
}
|
||||
|
||||
NEXT_NODE:
|
||||
for (NodeMetaData nodeMetaData : nodeMetaDatas)
|
||||
for (NodeMetaData nodeMetaData : nodesMetaDataFromRepository.get())
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
|
||||
@@ -2236,7 +2255,7 @@ public class SolrInformationServer implements InformationServer
|
||||
dataModel.getIndexedFieldNamesForProperty(propertyQName).getFields()
|
||||
.stream()
|
||||
.filter(field -> field.getField().startsWith("text@sd___@"))
|
||||
.forEach(field -> addStringProperty(valueHolder, field, value, locale, definition));
|
||||
.forEach(field -> addStringProperty(valueHolder, field, value, locale));
|
||||
} else
|
||||
{
|
||||
dataModel.getIndexedFieldNamesForProperty(propertyQName).getFields()
|
||||
@@ -2245,7 +2264,7 @@ public class SolrInformationServer implements InformationServer
|
||||
{
|
||||
setUnitOfTimeFields(valueHolder, field.getField(), value.getValue(), definition.getDataType());
|
||||
}
|
||||
addStringProperty(valueHolder, field, value, locale, definition);
|
||||
addStringProperty(valueHolder, field, value, locale);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3247,17 +3266,10 @@ public class SolrInformationServer implements InformationServer
|
||||
*/
|
||||
private String addTenantToAuthority(String authority, String tenant)
|
||||
{
|
||||
switch (AuthorityType.getAuthorityType(authority))
|
||||
AuthorityType authorityType = AuthorityType.getAuthorityType(authority);
|
||||
if ((authorityType == GROUP || authorityType == EVERYONE || authorityType == GUEST) && !tenant.isEmpty())
|
||||
{
|
||||
case GROUP:
|
||||
case EVERYONE:
|
||||
case GUEST:
|
||||
if (tenant.length() != 0)
|
||||
{
|
||||
return authority + "@" + tenant;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
return authority + "@" + tenant;
|
||||
}
|
||||
return authority;
|
||||
}
|
||||
@@ -3424,11 +3436,11 @@ public class SolrInformationServer implements InformationServer
|
||||
nmdp.setIncludeTxnId(true);
|
||||
nmdp.setMaxResults(1);
|
||||
// Gets only one
|
||||
Collection<NodeMetaData> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
Optional<Collection<NodeMetaData>> nodeMetaDatas = getNodesMetaDataFromRepository(nmdp);
|
||||
|
||||
if (!nodeMetaDatas.isEmpty())
|
||||
if (nodeMetaDatas.isPresent() && !nodeMetaDatas.get().isEmpty())
|
||||
{
|
||||
NodeMetaData nodeMetaData = nodeMetaDatas.iterator().next();
|
||||
NodeMetaData nodeMetaData = nodeMetaDatas.get().iterator().next();
|
||||
|
||||
// Only cascade update nods we know can not have changed and must be in this shard
|
||||
// Node in the current TX will be explicitly updated in the outer loop
|
||||
@@ -3737,7 +3749,7 @@ public class SolrInformationServer implements InformationServer
|
||||
return "\u0000" + locale.getLanguage() + "\u0000" + property.getValue();
|
||||
}
|
||||
|
||||
private void addStringProperty(BiConsumer<String, Object> consumer, FieldInstance field, StringPropertyValue property, PropertyValue localeProperty, PropertyDefinition definition)
|
||||
private void addStringProperty(BiConsumer<String, Object> consumer, FieldInstance field, StringPropertyValue property, PropertyValue localeProperty)
|
||||
{
|
||||
consumer.accept(field.getField(), field.isLocalised() ? getLocalisedValue(property, localeProperty) : property.getValue());
|
||||
}
|
||||
@@ -3909,21 +3921,30 @@ public class SolrInformationServer implements InformationServer
|
||||
return nodeMetaData;
|
||||
}
|
||||
|
||||
private Collection<NodeMetaData> getNodesMetaDataFromRepository(NodeMetaDataParameters parameters)
|
||||
/**
|
||||
* Get the metadata for the specified nodes from the repository.
|
||||
*
|
||||
* @param parameters A parameters object containing either a list of nodes ({@link NodeMetaDataParameters#getNodeIds})
|
||||
* or a node range ({@link NodeMetaDataParameters#getFromNodeId} and {@link NodeMetaDataParameters#getToNodeId}).
|
||||
* @return Either the metadata returned by the repository, or null if there was a problem.
|
||||
*/
|
||||
private Optional<Collection<NodeMetaData>> getNodesMetaDataFromRepository(NodeMetaDataParameters parameters)
|
||||
{
|
||||
Collection<NodeMetaData> nodeMetaDataCollection = null;
|
||||
try
|
||||
{
|
||||
return notNullOrEmpty(repositoryClient.getNodesMetaData(parameters));
|
||||
return Optional.of(notNullOrEmpty(repositoryClient.getNodesMetaData(parameters)));
|
||||
}
|
||||
catch (JSONException exception)
|
||||
{
|
||||
// Nothing to be done here: the exception has been already logged in repositoryClient
|
||||
return Collections.emptyList();
|
||||
// The exception has been already logged in repositoryClient and could be huge. Simply log a reference to it here.
|
||||
LOGGER.debug("JSON exception swallowed by SolrInformationServer.");
|
||||
return empty();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LOGGER.error("Unable to get nodes metadata from repository. See the stacktrace below for further details.", exception);
|
||||
return Collections.emptyList();
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -32,9 +32,9 @@ public class CacheConstants
|
||||
// public static String ALFRESCO_READER_TO_ACL_IDS_CACHE = "alfrescoReaderToAclIdsCache";
|
||||
// public static String ALFRESCO_DENY_TO_ACL_IDS_CACHE = "alfrescoDenyToAclIdsCache";
|
||||
|
||||
public static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache";
|
||||
public static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache";
|
||||
public static String ALFRESCO_READER_CACHE = "alfrescoReaderCache";
|
||||
public static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache";
|
||||
public static String ALFRESCO_PATH_CACHE = "alfrescoPathCache";
|
||||
public final static String ALFRESCO_AUTHORITY_CACHE = "alfrescoAuthorityCache";
|
||||
public final static String ALFRESCO_OWNERLOOKUP_CACHE = "alfrescoOwnerCache";
|
||||
public final static String ALFRESCO_READER_CACHE = "alfrescoReaderCache";
|
||||
public final static String ALFRESCO_DENIED_CACHE = "alfrescoDeniedCache";
|
||||
public final static String ALFRESCO_PATH_CACHE = "alfrescoPathCache";
|
||||
}
|
||||
|
||||
+2
-4
@@ -5472,8 +5472,6 @@ public class Solr4QueryParser extends QueryParser implements QueryConstants
|
||||
throw new RuntimeException("Error analyzing multiTerm term: " + part, e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean analyzeRangeTerms = true;
|
||||
|
||||
protected Query newRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) {
|
||||
final BytesRef start;
|
||||
@@ -5482,13 +5480,13 @@ public class Solr4QueryParser extends QueryParser implements QueryConstants
|
||||
if (part1 == null) {
|
||||
start = null;
|
||||
} else {
|
||||
start = analyzeRangeTerms ? analyzeMultitermTerm(field, part1) : new BytesRef(part1);
|
||||
start = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part1) : new BytesRef(part1);
|
||||
}
|
||||
|
||||
if (part2 == null) {
|
||||
end = null;
|
||||
} else {
|
||||
end = analyzeRangeTerms ? analyzeMultitermTerm(field, part2) : new BytesRef(part2);
|
||||
end = getAnalyzeRangeTerms() ? analyzeMultitermTerm(field, part2) : new BytesRef(part2);
|
||||
}
|
||||
|
||||
final TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive);
|
||||
|
||||
-4
@@ -37,10 +37,6 @@ import org.apache.lucene.search.Weight;
|
||||
*/
|
||||
public class SolrContainerScorer extends Scorer
|
||||
{
|
||||
// Unused
|
||||
Weight weight;
|
||||
|
||||
|
||||
SolrContainerScorerDocIdSetIterator iterator;
|
||||
|
||||
|
||||
|
||||
+21
-59
@@ -43,8 +43,7 @@ public abstract class AbstractTracker implements Tracker
|
||||
static final long TIME_STEP_32_DAYS_IN_MS = 1000 * 60 * 60 * 24 * 32L;
|
||||
static final long TIME_STEP_1_HR_IN_MS = 60 * 60 * 1000L;
|
||||
static final String SHARD_METHOD_DBID = "DB_ID";
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTracker.class);
|
||||
|
||||
protected Properties props;
|
||||
protected SOLRAPIClient client;
|
||||
@@ -57,9 +56,6 @@ public abstract class AbstractTracker implements Tracker
|
||||
private int maxLiveSearchers;
|
||||
private volatile boolean shutdown = false;
|
||||
|
||||
private Semaphore runLock = new Semaphore(1, true);
|
||||
private Semaphore writeLock = new Semaphore(1, true);
|
||||
|
||||
protected volatile TrackerState state;
|
||||
protected int shardCount;
|
||||
protected int shardInstance;
|
||||
@@ -74,11 +70,6 @@ public abstract class AbstractTracker implements Tracker
|
||||
protected final Type type;
|
||||
protected final String trackerId;
|
||||
|
||||
/*
|
||||
* A thread handler can be used by subclasses, but they have to intentionally instantiate it.
|
||||
*/
|
||||
ThreadHandler threadHandler;
|
||||
|
||||
/**
|
||||
* Default constructor, strictly for testing.
|
||||
*/
|
||||
@@ -168,9 +159,9 @@ public abstract class AbstractTracker implements Tracker
|
||||
{
|
||||
String iterationId = "IT #" + System.currentTimeMillis();
|
||||
|
||||
if(runLock.availablePermits() == 0)
|
||||
if(getRunLock().availablePermits() == 0)
|
||||
{
|
||||
logger.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId);
|
||||
LOGGER.info("[{} / {} / {}] Tracker already registered.", coreName, trackerId, iterationId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -180,7 +171,7 @@ public abstract class AbstractTracker implements Tracker
|
||||
* The runLock ensures that for each tracker type (metadata, content, commit, cascade) only one tracker will
|
||||
* be running at a time.
|
||||
*/
|
||||
runLock.acquire();
|
||||
getRunLock().acquire();
|
||||
|
||||
if (state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false")))
|
||||
{
|
||||
@@ -191,7 +182,7 @@ public abstract class AbstractTracker implements Tracker
|
||||
{
|
||||
this.state = getTrackerState();
|
||||
|
||||
logger.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString());
|
||||
LOGGER.debug("[{} / {} / {}] Global Tracker State set to: {}", coreName, trackerId, iterationId, this.state.toString());
|
||||
this.state.setRunning(true);
|
||||
}
|
||||
else
|
||||
@@ -209,28 +200,25 @@ public abstract class AbstractTracker implements Tracker
|
||||
catch(IndexTrackingShutdownException t)
|
||||
{
|
||||
setRollback(true, t);
|
||||
logger.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t);
|
||||
LOGGER.info("[{} / {} / {}] Tracking cycle stopped. See the stacktrace below for further details.", coreName, trackerId, iterationId, t);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
setRollback(true, t);
|
||||
if (t instanceof SocketTimeoutException || t instanceof ConnectException)
|
||||
{
|
||||
logger.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t);
|
||||
}
|
||||
LOGGER.warn("[{} / {} / {}] Tracking communication timed out. See the stacktrace below for further details.", coreName, trackerId, iterationId);
|
||||
LOGGER.debug("[{} / {} / {}] Stack trace", coreName, trackerId, iterationId, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t);
|
||||
LOGGER.error("[{} / {} / {}] Tracking failure. See the stacktrace below for further details.", coreName, trackerId, iterationId, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
logger.error("[{} / {} / {}] Semaphore interruption. See the stacktrace below for further details.", coreName, trackerId, iterationId, e);
|
||||
LOGGER.error("[{} / {} / {}] Semaphore interruption. See the stacktrace below for further details.", coreName, trackerId, iterationId, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -240,7 +228,7 @@ public abstract class AbstractTracker implements Tracker
|
||||
state.setRunning(false);
|
||||
state.setCheck(false);
|
||||
});
|
||||
runLock.release();
|
||||
getRunLock().release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,30 +272,6 @@ public abstract class AbstractTracker implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows time for the scheduled asynchronous tasks to complete
|
||||
*/
|
||||
synchronized void waitForAsynchronous()
|
||||
{
|
||||
AbstractWorkerRunnable currentRunnable = this.threadHandler.peekHeadReindexWorker();
|
||||
while (currentRunnable != null)
|
||||
{
|
||||
checkShutdown();
|
||||
synchronized (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait(100);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
// Nothing to be done here
|
||||
}
|
||||
}
|
||||
currentRunnable = this.threadHandler.peekHeadReindexWorker();
|
||||
}
|
||||
}
|
||||
|
||||
int getMaxLiveSearchers()
|
||||
{
|
||||
return maxLiveSearchers;
|
||||
@@ -337,21 +301,19 @@ public abstract class AbstractTracker implements Tracker
|
||||
public void shutdown()
|
||||
{
|
||||
setShutdown(true);
|
||||
if(this.threadHandler != null)
|
||||
{
|
||||
threadHandler.shutDownThreadPool();
|
||||
}
|
||||
}
|
||||
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return this.writeLock;
|
||||
}
|
||||
/**
|
||||
* Trackers implementing this method should decide if the Write Lock is applied
|
||||
* globally for every Tracker Thread (static) or locally for each running Thread
|
||||
*/
|
||||
public abstract Semaphore getWriteLock();
|
||||
|
||||
Semaphore getRunLock()
|
||||
{
|
||||
return this.runLock;
|
||||
}
|
||||
/**
|
||||
* Trackers implementing this method should decide if the Run Lock is applied
|
||||
* globally for every Tracker Thread (static) or locally for each running Thread
|
||||
*/
|
||||
public abstract Semaphore getRunLock();
|
||||
|
||||
public Properties getProps()
|
||||
{
|
||||
|
||||
+56
-68
@@ -1,68 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
abstract class AbstractWorkerRunnable implements Runnable
|
||||
{
|
||||
protected final static Logger log = LoggerFactory.getLogger(AbstractWorkerRunnable.class);
|
||||
|
||||
QueueHandler queueHandler;
|
||||
|
||||
public AbstractWorkerRunnable(QueueHandler qh)
|
||||
{
|
||||
this.queueHandler = qh;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run()
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
boolean failed = true;
|
||||
Exception failCausedBy = null;
|
||||
try
|
||||
{
|
||||
doWork();
|
||||
failed = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("Index tracking batch hit an unrecoverable error ", e);
|
||||
failCausedBy = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Triple check that we get the queue state right
|
||||
queueHandler.removeFromQueueAndProdHead(this);
|
||||
if(failed)
|
||||
{
|
||||
onFail(failCausedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void doWork() throws Exception;
|
||||
|
||||
abstract protected void onFail(Throwable failCausedBy);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Alfresco Software Limited.
|
||||
* This file is part of Alfresco
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Synchronous Tracking Worker.
|
||||
*
|
||||
* @author aborroy
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractWorker
|
||||
{
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(AbstractWorker.class);
|
||||
|
||||
public void run()
|
||||
{
|
||||
boolean failed = true;
|
||||
Exception failCausedBy = null;
|
||||
try
|
||||
{
|
||||
doWork();
|
||||
failed = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Index tracking batch hit an unrecoverable error ", e);
|
||||
failCausedBy = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (failed)
|
||||
{
|
||||
onFail(failCausedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void doWork() throws Exception;
|
||||
|
||||
abstract protected void onFail(Throwable failCausedBy);
|
||||
}
|
||||
+165
-202
@@ -20,13 +20,22 @@ package org.alfresco.solr.tracker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.httpclient.AuthenticationException;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
@@ -56,8 +65,11 @@ public class AclTracker extends AbstractTracker
|
||||
{
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(AclTracker.class);
|
||||
|
||||
private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 100;
|
||||
private static final int DEFAULT_ACL_BATCH_SIZE = 10;
|
||||
private static final int DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE = 2000;
|
||||
private static final int DEFAULT_ACL_BATCH_SIZE = 100;
|
||||
private static final int DEFAULT_ACL_TRACKER_MAX_PARALLELISM = 32;
|
||||
|
||||
private int aclTrackerParallelism;
|
||||
|
||||
private int changeSetAclsBatchSize = DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE;
|
||||
private int aclBatchSize = DEFAULT_ACL_BATCH_SIZE;
|
||||
@@ -69,6 +81,23 @@ public class AclTracker extends AbstractTracker
|
||||
private ConcurrentLinkedQueue<Long> aclsToIndex = new ConcurrentLinkedQueue<Long>();
|
||||
private ConcurrentLinkedQueue<Long> aclsToPurge = new ConcurrentLinkedQueue<Long>();
|
||||
private DocRouter docRouter;
|
||||
|
||||
private ForkJoinPool forkJoinPool;
|
||||
|
||||
// Share run and write locks across all AclTracker threads
|
||||
private static Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor, for testing.
|
||||
@@ -82,11 +111,18 @@ public class AclTracker extends AbstractTracker
|
||||
String coreName, InformationServer informationServer)
|
||||
{
|
||||
super(p, client, coreName, informationServer, Tracker.Type.ACL);
|
||||
changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize", "100"));
|
||||
aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", "10"));
|
||||
changeSetAclsBatchSize = Integer.parseInt(p.getProperty("alfresco.changeSetAclsBatchSize",
|
||||
String.valueOf(DEFAULT_CHANGE_SET_ACLS_BATCH_SIZE)));
|
||||
aclBatchSize = Integer.parseInt(p.getProperty("alfresco.aclBatchSize", String.valueOf(DEFAULT_ACL_BATCH_SIZE)));
|
||||
shardMethod = p.getProperty("shard.method", SHARD_METHOD_DBID);
|
||||
docRouter = DocRouterFactory.getRouter(p, ShardMethodEnum.getShardMethod(shardMethod));
|
||||
threadHandler = new ThreadHandler(p, coreName, "AclTracker");
|
||||
|
||||
aclTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.aclTrackerMaxParallelism",
|
||||
String.valueOf(DEFAULT_ACL_TRACKER_MAX_PARALLELISM)));
|
||||
forkJoinPool = new ForkJoinPool(aclTrackerParallelism);
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,7 +275,7 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
}
|
||||
|
||||
protected void purgeAclChangeSets() throws AuthenticationException, IOException, JSONException
|
||||
protected void purgeAclChangeSets() throws IOException, JSONException
|
||||
{
|
||||
while (aclChangeSetsToPurge.peek() != null)
|
||||
{
|
||||
@@ -254,7 +290,7 @@ public class AclTracker extends AbstractTracker
|
||||
|
||||
}
|
||||
|
||||
protected void purgeAcls() throws AuthenticationException, IOException, JSONException
|
||||
protected void purgeAcls() throws IOException, JSONException
|
||||
{
|
||||
while (aclsToPurge.peek() != null)
|
||||
{
|
||||
@@ -322,10 +358,6 @@ public class AclTracker extends AbstractTracker
|
||||
|
||||
/**
|
||||
* Checks the first and last TX time
|
||||
* @param state the state of this tracker
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void checkRepoAndIndexConsistency(TrackerState state) throws AuthenticationException, IOException, JSONException
|
||||
{
|
||||
@@ -342,13 +374,13 @@ public class AclTracker extends AbstractTracker
|
||||
AclChangeSet firstChangeSet = firstChangeSets.getAclChangeSets().get(0);
|
||||
long firstChangeSetCommitTime = firstChangeSet.getCommitTimeMs();
|
||||
state.setLastGoodChangeSetCommitTimeInIndex(firstChangeSetCommitTime);
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets, state);
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.isCheckedFirstAclTransactionTime())
|
||||
{
|
||||
firstChangeSets = client.getAclChangeSets(null, 0l, null, 2000L, 1);
|
||||
firstChangeSets = client.getAclChangeSets(null, 0L, null, 2000L, 1);
|
||||
if (!firstChangeSets.getAclChangeSets().isEmpty())
|
||||
{
|
||||
AclChangeSet firstAclChangeSet= firstChangeSets.getAclChangeSets().get(0);
|
||||
@@ -381,10 +413,10 @@ public class AclTracker extends AbstractTracker
|
||||
{
|
||||
if (firstChangeSets == null)
|
||||
{
|
||||
firstChangeSets = client.getAclChangeSets(null, 0l, null, 2000L, 1);
|
||||
firstChangeSets = client.getAclChangeSets(null, 0L, null, 2000L, 1);
|
||||
}
|
||||
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets, state);
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(firstChangeSets.getAclChangeSets(), state);
|
||||
Long maxChangeSetCommitTimeInRepo = firstChangeSets.getMaxChangeSetCommitTime();
|
||||
Long maxChangeSetIdInRepo = firstChangeSets.getMaxChangeSetId();
|
||||
if (maxChangeSetCommitTimeInRepo != null && maxChangeSetIdInRepo != null)
|
||||
@@ -464,7 +496,7 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
else
|
||||
{
|
||||
HashSet<AclChangeSet> alreadyFound = new HashSet<AclChangeSet>(changeSetsFound.getDeque());
|
||||
HashSet<AclChangeSet> alreadyFound = new HashSet<>(changeSetsFound.getDeque());
|
||||
for(AclChangeSet aclChangeSet : aclChangeSets.getAclChangeSets())
|
||||
{
|
||||
if(!alreadyFound.contains(aclChangeSet))
|
||||
@@ -499,7 +531,7 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
|
||||
IOpenBitSet aclTxIdsInDb = infoSrv.getOpenBitSetInstance();
|
||||
Long lastAclTxCommitTime = Long.valueOf(firstChangeSetCommitTimex);
|
||||
long lastAclTxCommitTime = firstChangeSetCommitTimex;
|
||||
if (fromTime != null)
|
||||
{
|
||||
lastAclTxCommitTime = fromTime;
|
||||
@@ -509,7 +541,7 @@ public class AclTracker extends AbstractTracker
|
||||
Long minAclTxId = null;
|
||||
long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention();
|
||||
AclChangeSets aclTransactions;
|
||||
BoundedDeque<AclChangeSet> changeSetsFound = new BoundedDeque<AclChangeSet>(100);
|
||||
BoundedDeque<AclChangeSet> changeSetsFound = new BoundedDeque<>(100);
|
||||
DO: do
|
||||
{
|
||||
aclTransactions = getSomeAclChangeSets(changeSetsFound, lastAclTxCommitTime, TIME_STEP_1_HR_IN_MS, 2000,
|
||||
@@ -519,14 +551,14 @@ public class AclTracker extends AbstractTracker
|
||||
// include
|
||||
if (toTime != null)
|
||||
{
|
||||
if (set.getCommitTimeMs() > toTime.longValue())
|
||||
if (set.getCommitTimeMs() > toTime)
|
||||
{
|
||||
break DO;
|
||||
}
|
||||
}
|
||||
if (toAclTx != null)
|
||||
{
|
||||
if (set.getId() > toAclTx.longValue())
|
||||
if (set.getId() > toAclTx)
|
||||
{
|
||||
break DO;
|
||||
}
|
||||
@@ -552,44 +584,11 @@ public class AclTracker extends AbstractTracker
|
||||
return this.infoSrv.reportAclTransactionsInIndex(minAclTxId, aclTxIdsInDb, maxAclTxId);
|
||||
}
|
||||
|
||||
|
||||
public List<Node> getFullNodesForDbTransaction(Long txid)
|
||||
{
|
||||
try
|
||||
{
|
||||
GetNodesParameters gnp = new GetNodesParameters();
|
||||
ArrayList<Long> txs = new ArrayList<Long>();
|
||||
txs.add(txid);
|
||||
gnp.setTransactionIds(txs);
|
||||
gnp.setStoreProtocol(storeRef.getProtocol());
|
||||
gnp.setStoreIdentifier(storeRef.getIdentifier());
|
||||
return client.getNodes(gnp, Integer.MAX_VALUE);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get nodes", e);
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get nodes", e);
|
||||
}
|
||||
catch (AuthenticationException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get nodes", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param acltxid Long
|
||||
* @return List<Long>
|
||||
**/
|
||||
public List<Long> getAclsForDbAclTransaction(Long acltxid)
|
||||
{
|
||||
try
|
||||
{
|
||||
ArrayList<Long> answer = new ArrayList<Long>();
|
||||
ArrayList<Long> answer = new ArrayList<>();
|
||||
AclChangeSets changeSet = client.getAclChangeSets(null, acltxid, null, acltxid+1, 1);
|
||||
List<Acl> acls = client.getAcls(changeSet.getAclChangeSets(), null, Integer.MAX_VALUE);
|
||||
for (Acl acl : acls)
|
||||
@@ -598,15 +597,7 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get acls", e);
|
||||
}
|
||||
catch (JSONException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get acls", e);
|
||||
}
|
||||
catch (AuthenticationException e)
|
||||
catch (IOException | JSONException | AuthenticationException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to get acls", e);
|
||||
}
|
||||
@@ -634,9 +625,14 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
* Every ACL Change Set contains a list of ACLs to be indexed.
|
||||
* This method gets ACL Change Sets from Alfresco Repository to be indexed.
|
||||
*
|
||||
* The indexing is performed in batches of ACL Change Sets and the Tracker Status
|
||||
* is updated in batched of ACLs.
|
||||
*
|
||||
* Tracker Status contains the Commit Time from the latest ACL Change Set indexed,
|
||||
* so new operations can be retrieved from Repository starting with that time.
|
||||
*/
|
||||
protected void trackAclChangeSets() throws AuthenticationException, IOException, JSONException
|
||||
{
|
||||
@@ -646,9 +642,10 @@ public class AclTracker extends AbstractTracker
|
||||
boolean upToDate = false;
|
||||
AclChangeSets aclChangeSets;
|
||||
BoundedDeque<AclChangeSet> changeSetsFound = new BoundedDeque<AclChangeSet>(100);
|
||||
HashSet<AclChangeSet> changeSetsIndexed = new LinkedHashSet<AclChangeSet>();
|
||||
long totalAclCount = 0;
|
||||
int aclCount = 0;
|
||||
int aclCount;
|
||||
|
||||
LOGGER.info("{}-[CORE {}] <init> Tracking ACLs", Thread.currentThread().getId(), coreName);
|
||||
|
||||
do
|
||||
{
|
||||
@@ -664,12 +661,12 @@ public class AclTracker extends AbstractTracker
|
||||
|
||||
this.state = getTrackerState();
|
||||
|
||||
Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, state.getLastGoodChangeSetCommitTimeInIndex());
|
||||
Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound,
|
||||
state.getLastChangeSetCommitTimeOnServer() == 0 ? state.getLastGoodChangeSetCommitTimeInIndex()
|
||||
: state.getLastChangeSetCommitTimeOnServer());
|
||||
aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, 2000,
|
||||
state.getTimeToStopIndexing());
|
||||
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(aclChangeSets, state);
|
||||
|
||||
if (aclChangeSets.getAclChangeSets().size() > 0)
|
||||
{
|
||||
LOGGER.info("{}-[CORE {}] Found {} ACL change sets after lastTxCommitTime {}, ACL Change Sets from {} to {}",
|
||||
@@ -686,90 +683,63 @@ public class AclTracker extends AbstractTracker
|
||||
Thread.currentThread().getId(), coreName, fromCommitTime);
|
||||
}
|
||||
|
||||
ArrayList<AclChangeSet> changeSetBatch = new ArrayList<AclChangeSet>();
|
||||
for (int i = 0; i < aclChangeSets.getAclChangeSets().size(); i++)
|
||||
{
|
||||
|
||||
AclChangeSet changeSet = aclChangeSets.getAclChangeSets().get(i);
|
||||
|
||||
boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() &&
|
||||
infoSrv.aclChangeSetInIndex(changeSet.getId(), true));
|
||||
|
||||
if (isInIndex)
|
||||
{
|
||||
// Logging progress for large ACL Change Set tracking every 100 tracked ACLs
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Tracking {} of {} ACL Change Sets. Change Set Id was already indexed: {}",
|
||||
Thread.currentThread().getId(), coreName, i + 1, aclChangeSets.getAclChangeSets().size(), changeSet.getId());
|
||||
}
|
||||
changeSetsFound.add(changeSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Logging progress for ACL Change Set
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Tracking {} of {} ACL Change Sets. Current Change Set Id to be indexed: {}",
|
||||
Thread.currentThread().getId(), coreName, i + 1, aclChangeSets.getAclChangeSets().size(), changeSet.getId());
|
||||
}
|
||||
|
||||
// Make sure we do not go ahead of where we started - we will check the holes here
|
||||
// correctly next time
|
||||
if (changeSet.getCommitTimeMs() > state.getTimeToStopIndexing()) {
|
||||
upToDate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
changeSetBatch.add(changeSet);
|
||||
if (getAclCount(changeSetBatch) > changeSetAclsBatchSize) {
|
||||
aclCount += indexBatchOfChangeSets(changeSetBatch);
|
||||
totalAclCount += aclCount;
|
||||
|
||||
for (AclChangeSet scheduled : changeSetBatch) {
|
||||
changeSetsFound.add(scheduled);
|
||||
changeSetsIndexed.add(scheduled);
|
||||
// Ignore indexed ACL Change Sets
|
||||
aclChangeSets = new AclChangeSets(aclChangeSets.getAclChangeSets().stream()
|
||||
.filter(changeSet -> {
|
||||
try
|
||||
{
|
||||
boolean isInIndex = (changeSet.getCommitTimeMs() <= state.getLastIndexedChangeSetCommitTime() &&
|
||||
infoSrv.aclChangeSetInIndex(changeSet.getId(), true));
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Skipping change Set Id {} as it was already indexed",
|
||||
Thread.currentThread().getId(), coreName, changeSet.getId());
|
||||
}
|
||||
return !isInIndex;
|
||||
}
|
||||
changeSetBatch.clear();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn(
|
||||
"{}-[CORE {}] Error catched while checking if ACL Change Set {} was in index",
|
||||
Thread.currentThread().getId(), coreName, changeSet.getId(), e);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
if (aclCount > batchCount) {
|
||||
if (super.infoSrv.getRegisteredSearcherCount() < getMaxLiveSearchers()) {
|
||||
indexAclChangeSetAfterAsynchronous(changeSetsIndexed, state);
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedAclTime(aclCount, endElapsed - startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
aclCount = 0;
|
||||
}
|
||||
}
|
||||
checkShutdown();
|
||||
}
|
||||
|
||||
if (!changeSetBatch.isEmpty()) {
|
||||
if (getAclCount(changeSetBatch) > 0) {
|
||||
aclCount += indexBatchOfChangeSets(changeSetBatch);
|
||||
totalAclCount += aclCount;
|
||||
}
|
||||
|
||||
for (AclChangeSet scheduled : changeSetBatch) {
|
||||
changeSetsFound.add(scheduled);
|
||||
changeSetsIndexed.add(scheduled);
|
||||
}
|
||||
changeSetBatch.clear();
|
||||
}
|
||||
|
||||
if(changeSetsIndexed.size() > 0)
|
||||
// Make sure we do not go ahead of where we started - we will check the holes here
|
||||
// correctly next time
|
||||
if (aclChangeSets.getAclChangeSets().stream().anyMatch(changeSet -> changeSet.getCommitTimeMs() > state.getTimeToStopIndexing()))
|
||||
{
|
||||
indexAclChangeSetAfterAsynchronous(changeSetsIndexed, state);
|
||||
break;
|
||||
}
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
Collection<List<AclChangeSet>> changeSetBatches = aclChangeSets.getAclChangeSets().stream()
|
||||
.collect(Collectors.groupingBy(it -> counter.getAndAdd(it.getAclCount()) / changeSetAclsBatchSize)).values();
|
||||
|
||||
|
||||
for (List<AclChangeSet> changeSetBatch : changeSetBatches)
|
||||
{
|
||||
aclCount = indexBatchOfChangeSets(changeSetBatch);
|
||||
for (AclChangeSet indexed : changeSetBatch)
|
||||
{
|
||||
changeSetsFound.add(indexed);
|
||||
}
|
||||
// Update last committed transactions
|
||||
setLastChangeSetIdAndCommitTimeInTrackerState(changeSetBatch, state);
|
||||
indexAclChangeSetAfterWorker(changeSetBatch, state);
|
||||
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedAclTime(aclCount, endElapsed-startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
aclCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
catch(InterruptedException | ExecutionException e)
|
||||
{
|
||||
throw new IOException(e);
|
||||
}
|
||||
@@ -781,32 +751,39 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
while ((aclChangeSets.getAclChangeSets().size() > 0) && (upToDate == false));
|
||||
|
||||
LOGGER.info("{}-[CORE {}] Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount);
|
||||
|
||||
LOGGER.info("{}-[CORE {}] <end> Tracked {} ACLs", Thread.currentThread().getId(), coreName, totalAclCount);
|
||||
|
||||
}
|
||||
|
||||
private void setLastChangeSetIdAndCommitTimeInTrackerState(AclChangeSets aclChangeSets, TrackerState state)
|
||||
private void setLastChangeSetIdAndCommitTimeInTrackerState(List<AclChangeSet> aclChangeSets, TrackerState state)
|
||||
{
|
||||
Long maxChangeSetCommitTime = aclChangeSets.getMaxChangeSetCommitTime();
|
||||
if(maxChangeSetCommitTime != null)
|
||||
|
||||
if (!aclChangeSets.isEmpty())
|
||||
{
|
||||
state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime);
|
||||
}
|
||||
|
||||
Long maxChangeSetId = aclChangeSets.getMaxChangeSetId();
|
||||
if(maxChangeSetId != null)
|
||||
{
|
||||
state.setLastChangeSetIdOnServer(maxChangeSetId);
|
||||
Long maxChangeSetCommitTime = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).get().getCommitTimeMs();
|
||||
if(maxChangeSetCommitTime != null)
|
||||
{
|
||||
state.setLastChangeSetCommitTimeOnServer(maxChangeSetCommitTime);
|
||||
}
|
||||
|
||||
Long maxChangeSetId = aclChangeSets.stream().max(Comparator.comparing(AclChangeSet::getId)).get().getId();
|
||||
if(maxChangeSetId != null)
|
||||
{
|
||||
state.setLastChangeSetIdOnServer(maxChangeSetId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void indexAclChangeSetAfterAsynchronous(HashSet<AclChangeSet> changeSetsIndexed, TrackerState state)
|
||||
/**
|
||||
* Index ACL Change Set transaction after ACLs has been indexed by the worker
|
||||
*/
|
||||
private void indexAclChangeSetAfterWorker(Collection<AclChangeSet> changeSetsIndexed, TrackerState state)
|
||||
throws IOException
|
||||
{
|
||||
waitForAsynchronous();
|
||||
for (AclChangeSet set : changeSetsIndexed)
|
||||
{
|
||||
super.infoSrv.indexAclTransaction(set, true);
|
||||
infoSrv.indexAclTransaction(set, true);
|
||||
// Acl change sets are ordered by commit time and tie-broken by id
|
||||
if (set.getCommitTimeMs() > state.getLastIndexedChangeSetCommitTime()
|
||||
|| set.getCommitTimeMs() == state.getLastIndexedChangeSetCommitTime()
|
||||
@@ -817,8 +794,6 @@ public class AclTracker extends AbstractTracker
|
||||
}
|
||||
trackerStats.addChangeSetAcls(set.getAclCount());
|
||||
}
|
||||
changeSetsIndexed.clear();
|
||||
//super.infoSrv.commit();
|
||||
}
|
||||
|
||||
private int getAclCount(List<AclChangeSet> changeSetBatch)
|
||||
@@ -831,19 +806,20 @@ public class AclTracker extends AbstractTracker
|
||||
return count;
|
||||
}
|
||||
|
||||
private int indexBatchOfChangeSets(List<AclChangeSet> changeSetBatch) throws AuthenticationException, IOException, JSONException
|
||||
{
|
||||
int aclCount = 0;
|
||||
ArrayList<AclChangeSet> nonEmptyChangeSets = new ArrayList<AclChangeSet>(changeSetBatch.size());
|
||||
for (AclChangeSet set : changeSetBatch)
|
||||
{
|
||||
if (set.getAclCount() > 0)
|
||||
{
|
||||
nonEmptyChangeSets.add(set);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Index ACLs from ACL Change Sets contained in changeSetBatch
|
||||
* When total ACL indexed count is greater than the specified for a single execution
|
||||
* (maxAclsPerExecution), no more ACL Change Sets are processed.
|
||||
*
|
||||
* @param changeSetBatch List of ACL Change Sets to be indexed
|
||||
* @return List of ACL Change Set indexed and Count of ACL indexed
|
||||
*/
|
||||
private int indexBatchOfChangeSets(List<AclChangeSet> changeSetBatch) throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException {
|
||||
// Exclude ACL Change Set with no ACLs inside
|
||||
List<AclChangeSet> nonEmptyChangeSets = changeSetBatch.stream()
|
||||
.filter(set -> set.getAclCount() > 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ArrayList<Acl> aclBatch = new ArrayList<Acl>();
|
||||
List<Acl> acls = client.getAcls(nonEmptyChangeSets, null, Integer.MAX_VALUE);
|
||||
|
||||
if (LOGGER.isDebugEnabled())
|
||||
@@ -851,40 +827,27 @@ public class AclTracker extends AbstractTracker
|
||||
LOGGER.debug("{}-[CORE {}] Found {} Acls from Acl Change Sets: {}", Thread.currentThread().getId(),
|
||||
coreName, acls.size(), nonEmptyChangeSets);
|
||||
}
|
||||
|
||||
List<List<Acl>> aclBatches = Lists.partition(acls, aclBatchSize);
|
||||
|
||||
for (Acl acl : acls)
|
||||
{
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Adding ACL {} to scheduled indexing job", Thread.currentThread().getId(),
|
||||
coreName, acl.toString());
|
||||
}
|
||||
aclBatch.add(acl);
|
||||
if (aclBatch.size() > aclBatchSize)
|
||||
{
|
||||
aclCount += aclBatch.size();
|
||||
AclIndexWorkerRunnable aiwr = new AclIndexWorkerRunnable(this.threadHandler, aclBatch);
|
||||
this.threadHandler.scheduleTask(aiwr);
|
||||
aclBatch = new ArrayList<Acl>();
|
||||
}
|
||||
}
|
||||
if (aclBatch.size() > 0)
|
||||
{
|
||||
aclCount += aclBatch.size();
|
||||
AclIndexWorkerRunnable aiwr = new AclIndexWorkerRunnable(this.threadHandler, aclBatch);
|
||||
this.threadHandler.scheduleTask(aiwr);
|
||||
aclBatch = new ArrayList<Acl>();
|
||||
}
|
||||
return aclCount;
|
||||
return forkJoinPool.submit(() ->
|
||||
aclBatches.parallelStream().map(batch -> {
|
||||
new AclIndexWorker(batch).run();
|
||||
return batch.size();
|
||||
}).reduce(0, Integer::sum)
|
||||
).get();
|
||||
}
|
||||
|
||||
class AclIndexWorkerRunnable extends AbstractWorkerRunnable
|
||||
|
||||
/**
|
||||
* ACL Indexer
|
||||
*/
|
||||
class AclIndexWorker extends AbstractWorker
|
||||
{
|
||||
List<Acl> acls;
|
||||
|
||||
AclIndexWorkerRunnable(QueueHandler queueHandler, List<Acl> acls)
|
||||
AclIndexWorker(List<Acl> acls)
|
||||
{
|
||||
super(queueHandler);
|
||||
this.acls = acls;
|
||||
}
|
||||
|
||||
@@ -907,7 +870,7 @@ public class AclTracker extends AbstractTracker
|
||||
|
||||
private List<Acl> filterAcls(List<Acl> acls)
|
||||
{
|
||||
ArrayList<Acl> filteredList = new ArrayList<Acl>(acls.size());
|
||||
ArrayList<Acl> filteredList = new ArrayList<>(acls.size());
|
||||
for(Acl acl : acls)
|
||||
{
|
||||
if(docRouter.routeAcl(shardCount, shardInstance, acl))
|
||||
|
||||
+99
-41
@@ -18,20 +18,28 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.alfresco.httpclient.AuthenticationException;
|
||||
import org.alfresco.solr.InformationServer;
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.alfresco.solr.client.Transaction;
|
||||
import org.apache.commons.codec.EncoderException;
|
||||
import org.json.JSONException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,8 +54,29 @@ import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
|
||||
protected final static Logger log = LoggerFactory.getLogger(CascadeTracker.class);
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(CascadeTracker.class);
|
||||
|
||||
private static final int DEFAULT_CASCADE_TRACKER_MAX_PARALLELISM = 32;
|
||||
private static final int DEFAULT_CASCADE_NODE_BATCH_SIZE = 10;
|
||||
|
||||
|
||||
// Share run and write locks across all CascadeTracker threads
|
||||
private static Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private int cascadeBatchSize;
|
||||
private ForkJoinPool forkJoinPool;
|
||||
private int cascadeTrackerParallelism;
|
||||
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
|
||||
public CascadeTracker(Properties p, SOLRAPIClient client, String coreName,
|
||||
@@ -55,7 +84,15 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
super(p, client, coreName, informationServer, Tracker.Type.CASCADE);
|
||||
|
||||
threadHandler = new ThreadHandler(p, coreName, "CascadeTracker");
|
||||
cascadeTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.cascadeTrackerMaxParallelism",
|
||||
String.valueOf(DEFAULT_CASCADE_TRACKER_MAX_PARALLELISM)));
|
||||
|
||||
cascadeBatchSize = Integer.parseInt(p.getProperty("alfresco.cascadeNodeBatchSize",
|
||||
String.valueOf(DEFAULT_CASCADE_NODE_BATCH_SIZE)));;
|
||||
|
||||
forkJoinPool = new ForkJoinPool(cascadeTrackerParallelism);
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
CascadeTracker()
|
||||
@@ -64,7 +101,7 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException, EncoderException
|
||||
protected void doTrack(String iterationId) throws IOException, JSONException
|
||||
{
|
||||
// MetadataTracker must wait until ModelTracker has run
|
||||
ModelTracker modelTracker = this.infoSrv.getAdminHandler().getTrackerRegistry().getModelTracker();
|
||||
@@ -74,38 +111,36 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
public void maintenance() throws Exception {
|
||||
|
||||
public void maintenance()
|
||||
{
|
||||
}
|
||||
|
||||
public boolean hasMaintenance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void trackRepository(String iterationId) throws IOException, AuthenticationException, JSONException, EncoderException
|
||||
private void trackRepository(String iterationId) throws IOException, JSONException
|
||||
{
|
||||
checkShutdown();
|
||||
processCascades(iterationId);
|
||||
}
|
||||
|
||||
private void updateTransactionsAfterAsynchronous(List<Transaction> txsIndexed)
|
||||
private void updateTransactionsAfterWorker(List<Transaction> txsIndexed)
|
||||
throws IOException
|
||||
{
|
||||
waitForAsynchronous();
|
||||
for (Transaction tx : txsIndexed)
|
||||
{
|
||||
super.infoSrv.updateTransaction(tx);
|
||||
}
|
||||
}
|
||||
|
||||
class CascadeIndexWorkerRunnable extends AbstractWorkerRunnable
|
||||
class CascadeIndexWorker extends AbstractWorker
|
||||
{
|
||||
InformationServer infoServer;
|
||||
List<NodeMetaData> nodes;
|
||||
|
||||
CascadeIndexWorkerRunnable(QueueHandler queueHandler, List<NodeMetaData> nodes, InformationServer infoServer)
|
||||
CascadeIndexWorker(List<NodeMetaData> nodes, InformationServer infoServer)
|
||||
{
|
||||
super(queueHandler);
|
||||
this.infoServer = infoServer;
|
||||
this.nodes = nodes;
|
||||
}
|
||||
@@ -119,11 +154,12 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
@Override
|
||||
protected void onFail(Throwable failCausedBy)
|
||||
{
|
||||
setRollback(true, failCausedBy);
|
||||
setRollback(true, failCausedBy);
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateState() {
|
||||
public void invalidateState()
|
||||
{
|
||||
super.invalidateState();
|
||||
infoSrv.setCleanCascadeTxnFloor(-1);
|
||||
}
|
||||
@@ -132,52 +168,68 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
int num = 50;
|
||||
List<Transaction> txBatch = null;
|
||||
long totalUpdatedDocs = 0;
|
||||
|
||||
do {
|
||||
try {
|
||||
getWriteLock().acquire();
|
||||
txBatch = infoSrv.getCascades(num);
|
||||
|
||||
if (txBatch.size() > 0)
|
||||
{
|
||||
LOGGER.info("{}-[CORE {}] Found {} transactions, transactions from {} to {}",
|
||||
Thread.currentThread().getId(),
|
||||
coreName,
|
||||
txBatch.size(),
|
||||
txBatch.get(0),
|
||||
txBatch.get(txBatch.size() - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("{}-[CORE {}] No transaction found",
|
||||
Thread.currentThread().getId(), coreName);
|
||||
}
|
||||
|
||||
if(txBatch.size() == 0) {
|
||||
//No transactions to process for cascades.
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<Long> txIds = new ArrayList<Long>();
|
||||
Set<Long> txIdSet = new HashSet<Long>();
|
||||
ArrayList<Long> txIds = new ArrayList<>();
|
||||
Set<Long> txIdSet = new HashSet<>();
|
||||
for (Transaction tx : txBatch) {
|
||||
txIds.add(tx.getId());
|
||||
txIdSet.add(tx.getId());
|
||||
}
|
||||
|
||||
List<NodeMetaData> nodeMetaDatas = infoSrv.getCascadeNodes(txIds);
|
||||
Integer processedCascades = 0;
|
||||
|
||||
if(nodeMetaDatas.size() > 0) {
|
||||
LinkedList<NodeMetaData> stack = new LinkedList<NodeMetaData>();
|
||||
stack.addAll(nodeMetaDatas);
|
||||
int batchSize = 10;
|
||||
List<List<NodeMetaData>> nodeBatches = Lists.partition(nodeMetaDatas, cascadeBatchSize);
|
||||
|
||||
do {
|
||||
List<NodeMetaData> batch = new ArrayList<NodeMetaData>();
|
||||
while (batch.size() < batchSize && stack.size() > 0) {
|
||||
batch.add(stack.removeFirst());
|
||||
}
|
||||
processedCascades = forkJoinPool.submit( () ->
|
||||
nodeBatches.parallelStream().map( batch -> {
|
||||
|
||||
CascadeIndexWorker worker = new CascadeIndexWorker(batch, infoSrv);
|
||||
worker.run();
|
||||
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
String nodes = notNullOrEmpty(batch).stream()
|
||||
.map(NodeMetaData::getId)
|
||||
.map(Object::toString)
|
||||
.collect(joining(","));
|
||||
LOGGER.trace("[{} / {} / {} / {}] Worker has been created for nodes {}", coreName, trackerId, iterationId, worker.hashCode(), nodes);
|
||||
}
|
||||
return batch.size();
|
||||
}).reduce(0, Integer::sum)
|
||||
).get();
|
||||
|
||||
|
||||
CascadeIndexWorkerRunnable worker = new CascadeIndexWorkerRunnable(this.threadHandler, batch, infoSrv);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
String nodes = notNullOrEmpty(batch).stream()
|
||||
.map(NodeMetaData::getId)
|
||||
.map(Object::toString)
|
||||
.collect(joining(","));
|
||||
logger.trace("[{} / {} / {} / {}] Worker has been created for nodes {}", coreName, trackerId, iterationId, worker.hashCode(), nodes);
|
||||
}
|
||||
this.threadHandler.scheduleTask(worker);
|
||||
}
|
||||
while (stack.size() > 0);
|
||||
}
|
||||
//Update the transaction records.
|
||||
updateTransactionsAfterAsynchronous(txBatch);
|
||||
updateTransactionsAfterWorker(txBatch);
|
||||
totalUpdatedDocs += processedCascades;
|
||||
}
|
||||
catch (AuthenticationException e)
|
||||
{
|
||||
@@ -191,11 +243,17 @@ public class CascadeTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
throw new IOException(e);
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
getWriteLock().release();
|
||||
}
|
||||
|
||||
} while(txBatch.size() > 0);
|
||||
|
||||
LOGGER.info("{}-[CORE {}] Updated {} DOCs", Thread.currentThread().getId(), coreName, totalUpdatedDocs);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+24
-4
@@ -23,8 +23,11 @@ import static java.util.Optional.empty;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.alfresco.solr.InformationServer;
|
||||
@@ -49,7 +52,21 @@ public class CommitTracker extends AbstractTracker
|
||||
private Optional<CascadeTracker> cascadeTracker = empty();
|
||||
private AtomicInteger rollbackCount = new AtomicInteger(0);
|
||||
|
||||
protected final static Logger log = LoggerFactory.getLogger(CommitTracker.class);
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(CommitTracker.class);
|
||||
|
||||
// Share run and write locks across all CommitTracker threads
|
||||
private static Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor, for testing.
|
||||
@@ -83,6 +100,9 @@ public class CommitTracker extends AbstractTracker
|
||||
commitInterval = Long.parseLong(p.getProperty("alfresco.commitInterval", "60000")); // Default: commit once per minute
|
||||
newSearcherInterval = Integer.parseInt(p.getProperty("alfresco.newSearcherInterval", "120000")); // Default: Open searchers every two minutes
|
||||
lastSearcherOpened = lastCommit = System.currentTimeMillis();
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
public boolean hasMaintenance() throws Exception
|
||||
@@ -194,17 +214,17 @@ public class CommitTracker extends AbstractTracker
|
||||
// Log reasons why the rollback is performed
|
||||
if (aclTracker.getRollbackCausedBy() != null)
|
||||
{
|
||||
log.warn("Rollback performed due to ACL Tracker error", aclTracker.getRollbackCausedBy());
|
||||
LOGGER.warn("Rollback performed due to ACL Tracker error", aclTracker.getRollbackCausedBy());
|
||||
}
|
||||
if (metadataTracker.getRollbackCausedBy() != null)
|
||||
{
|
||||
log.warn("Rollback performed due to Metadata Tracker error", metadataTracker.getRollbackCausedBy());
|
||||
LOGGER.warn("Rollback performed due to Metadata Tracker error", metadataTracker.getRollbackCausedBy());
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("Rollback failed", e);
|
||||
LOGGER.error("Rollback failed", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
+68
-46
@@ -16,15 +16,22 @@
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.alfresco.solr.AlfrescoSolrDataModel.TenantDbId;
|
||||
import org.alfresco.solr.InformationServer;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
|
||||
@@ -36,17 +43,45 @@ import static org.alfresco.solr.utils.Utils.notNullOrEmpty;
|
||||
*/
|
||||
public class ContentTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
private int contentReadBatchSize;
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(ContentTracker.class);
|
||||
|
||||
private static final int DEFAULT_CONTENT_TRACKER_MAX_PARALLELISM = 32;
|
||||
|
||||
private int contentTrackerParallelism;
|
||||
private int contentUpdateBatchSize;
|
||||
|
||||
// Share run and write locks across all ContentTracker threads
|
||||
private static final Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private ForkJoinPool forkJoinPool;
|
||||
|
||||
public ContentTracker(Properties p, SOLRAPIClient client, String coreName,
|
||||
InformationServer informationServer)
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
public ContentTracker(Properties p, SOLRAPIClient client, String coreName, InformationServer informationServer)
|
||||
{
|
||||
super(p, client, coreName, informationServer, Tracker.Type.CONTENT);
|
||||
contentReadBatchSize = Integer.parseInt(p.getProperty("alfresco.contentReadBatchSize", "100"));
|
||||
contentUpdateBatchSize = Integer.parseInt(p.getProperty("alfresco.contentUpdateBatchSize", "1000"));
|
||||
threadHandler = new ThreadHandler(p, coreName, "ContentTracker");
|
||||
int DEFAULT_CONTENT_UPDATE_BATCH_SIZE = 2000;
|
||||
|
||||
contentUpdateBatchSize = Integer.parseInt(p.getProperty("alfresco.contentUpdateBatchSize",
|
||||
String.valueOf(DEFAULT_CONTENT_UPDATE_BATCH_SIZE)));
|
||||
|
||||
contentTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.contentTrackerMaxParallelism",
|
||||
String.valueOf(DEFAULT_CONTENT_TRACKER_MAX_PARALLELISM)));
|
||||
|
||||
forkJoinPool = new ForkJoinPool(contentTrackerParallelism);
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
ContentTracker()
|
||||
@@ -62,51 +97,39 @@ public class ContentTracker extends AbstractTracker implements Tracker
|
||||
long startElapsed = System.nanoTime();
|
||||
|
||||
checkShutdown();
|
||||
final int ROWS = contentReadBatchSize;
|
||||
int start = 0;
|
||||
long totalDocs = 0L;
|
||||
checkShutdown();
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
getWriteLock().acquire();
|
||||
|
||||
List<TenantDbId> docs = notNullOrEmpty(infoSrv.getDocsWithUncleanContent(start, ROWS));
|
||||
List<TenantDbId> docs = notNullOrEmpty(this.infoSrv.getDocsWithUncleanContent());
|
||||
if (docs.isEmpty())
|
||||
{
|
||||
logger.debug("No unclean document has been detected in the current ContentTracker cycle.");
|
||||
LOGGER.trace("No unclean document has been detected in the current ContentTracker cycle.");
|
||||
break;
|
||||
}
|
||||
|
||||
int docsUpdatedSinceLastCommit = 0;
|
||||
for (TenantDbId doc : docs)
|
||||
List<List<TenantDbId>> docBatches = Lists.partition(docs, contentUpdateBatchSize);
|
||||
for (List<TenantDbId> batch : docBatches)
|
||||
{
|
||||
ContentIndexWorkerRunnable ciwr = new ContentIndexWorkerRunnable(super.threadHandler, doc, infoSrv);
|
||||
super.threadHandler.scheduleTask(ciwr);
|
||||
docsUpdatedSinceLastCommit++;
|
||||
Integer processedDocuments = forkJoinPool.submit(() ->
|
||||
// Parallel task here, for example
|
||||
batch.parallelStream().map(doc -> {
|
||||
ContentIndexWorkerRunnable ciwr = new ContentIndexWorkerRunnable(doc, infoSrv);
|
||||
ciwr.run();
|
||||
return 1;
|
||||
}).reduce(0, Integer::sum)
|
||||
).get();
|
||||
|
||||
if (docsUpdatedSinceLastCommit >= contentUpdateBatchSize)
|
||||
{
|
||||
super.waitForAsynchronous();
|
||||
checkShutdown();
|
||||
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedContentTime(docsUpdatedSinceLastCommit, endElapsed - startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
docsUpdatedSinceLastCommit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (docsUpdatedSinceLastCommit > 0)
|
||||
{
|
||||
super.waitForAsynchronous();
|
||||
checkShutdown();
|
||||
//this.infoSrv.commit();
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedContentTime(docsUpdatedSinceLastCommit, endElapsed - startElapsed);
|
||||
trackerStats.addElapsedContentTime(processedDocuments, endElapsed - startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
|
||||
}
|
||||
|
||||
totalDocs += docs.size();
|
||||
checkShutdown();
|
||||
}
|
||||
@@ -116,7 +139,8 @@ public class ContentTracker extends AbstractTracker implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Total number of docs with content updated: {}", totalDocs);
|
||||
LOGGER.info("{}-[CORE {}] Total number of docs with content updated: {} ", Thread.currentThread().getId(), coreName, totalDocs);
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -140,16 +164,14 @@ public class ContentTracker extends AbstractTracker implements Tracker
|
||||
this.infoSrv.setCleanContentTxnFloor(-1);
|
||||
}
|
||||
|
||||
class ContentIndexWorkerRunnable extends AbstractWorkerRunnable
|
||||
class ContentIndexWorkerRunnable extends AbstractWorker
|
||||
{
|
||||
InformationServer infoServer;
|
||||
TenantDbId docRef;
|
||||
|
||||
ContentIndexWorkerRunnable(QueueHandler queueHandler, TenantDbId docRef, InformationServer infoServer)
|
||||
ContentIndexWorkerRunnable(TenantDbId doc, InformationServer infoServer)
|
||||
{
|
||||
super(queueHandler);
|
||||
|
||||
this.docRef = docRef;
|
||||
this.docRef = doc;
|
||||
this.infoServer = infoServer;
|
||||
}
|
||||
|
||||
@@ -165,7 +187,7 @@ public class ContentTracker extends AbstractTracker implements Tracker
|
||||
protected void onFail(Throwable failCausedBy)
|
||||
{
|
||||
// This will be redone in future tracking operations
|
||||
log.warn("Content tracker failed due to {}", failCausedBy.getMessage(), failCausedBy);
|
||||
LOGGER.warn("Content tracker failed due to {}", failCausedBy.getMessage(), failCausedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -46,6 +46,8 @@ import org.alfresco.solr.NodeReport;
|
||||
import org.alfresco.solr.TrackerState;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
@@ -63,6 +65,7 @@ import java.util.Properties;
|
||||
*/
|
||||
public abstract class CoreStatePublisher extends AbstractTracker
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CoreStatePublisher.class);
|
||||
DocRouter docRouter;
|
||||
private final boolean isMaster;
|
||||
|
||||
@@ -118,7 +121,7 @@ public abstract class CoreStatePublisher extends AbstractTracker
|
||||
updateShardProperty();
|
||||
if (shardProperty.isEmpty())
|
||||
{
|
||||
logger.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName);
|
||||
LOGGER.warn("Sharding property {} was set to {}, but no such property was found.", SHARD_KEY_KEY, shardKeyName);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -134,11 +137,11 @@ public abstract class CoreStatePublisher extends AbstractTracker
|
||||
{
|
||||
if (updatedShardProperty.isEmpty())
|
||||
{
|
||||
logger.warn("The model defining {} property has been disabled", shardKeyName);
|
||||
LOGGER.warn("The model defining {} property has been disabled", shardKeyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("New {} property found for {} ", SHARD_KEY_KEY, shardKeyName);
|
||||
LOGGER.info("New {} property found for {}", SHARD_KEY_KEY, shardKeyName);
|
||||
}
|
||||
}
|
||||
shardProperty = updatedShardProperty;
|
||||
|
||||
+8
-6
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -44,14 +45,15 @@ public class DocRouterFactory
|
||||
LOGGER.info("Sharding via DB_ID");
|
||||
return new DBIDRouter();
|
||||
case DB_ID_RANGE:
|
||||
if(properties.containsKey(SHARD_RANGE_KEY))
|
||||
if(!properties.containsKey(SHARD_RANGE_KEY))
|
||||
{
|
||||
LOGGER.info("Sharding via DB_ID_RANGE");
|
||||
String[] pair =properties.getProperty(SHARD_RANGE_KEY).split("-");
|
||||
long start = Long.parseLong(pair[0]);
|
||||
long end = Long.parseLong(pair[1]);
|
||||
return new DBIDRangeRouter(start, end);
|
||||
throw new AlfrescoRuntimeException("DB_ID_RANGE sharding requires the " + SHARD_RANGE_KEY + " property to be set.");
|
||||
}
|
||||
LOGGER.info("Sharding via DB_ID_RANGE");
|
||||
String[] pair = properties.getProperty(SHARD_RANGE_KEY).split("-");
|
||||
long start = Long.parseLong(pair[0]);
|
||||
long end = Long.parseLong(pair[1]);
|
||||
return new DBIDRangeRouter(start, end);
|
||||
case ACL_ID:
|
||||
LOGGER.info("Sharding via ACL_ID");
|
||||
return new ACLIDMurmurRouter();
|
||||
|
||||
+377
-264
@@ -18,17 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID_RANGE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.httpclient.AuthenticationException;
|
||||
import org.alfresco.repo.index.shard.ShardState;
|
||||
@@ -49,17 +39,41 @@ import org.json.JSONException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.alfresco.repo.index.shard.ShardMethodEnum.DB_ID_RANGE;
|
||||
|
||||
/*
|
||||
* This tracks two things: transactions and metadata nodes
|
||||
* @author Ahmed Owian
|
||||
*/
|
||||
public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
{
|
||||
protected final static Logger log = LoggerFactory.getLogger(MetadataTracker.class);
|
||||
private static final int DEFAULT_TRANSACTION_DOCS_BATCH_SIZE = 100;
|
||||
private static final int DEFAULT_NODE_BATCH_SIZE = 10;
|
||||
private int transactionDocsBatchSize = DEFAULT_TRANSACTION_DOCS_BATCH_SIZE;
|
||||
private int nodeBatchSize = DEFAULT_NODE_BATCH_SIZE;
|
||||
protected final static Logger LOGGER = LoggerFactory.getLogger(MetadataTracker.class);
|
||||
|
||||
private static final int DEFAULT_METADATA_TRACKER_MAX_PARALLELISM = 32;
|
||||
private static final int DEFAULT_TRANSACTION_DOCS_BATCH_SIZE = 2000;
|
||||
private static final int DEFAULT_MAX_NUMBER_OF_TRANSACTIONS = 2000;
|
||||
private static final int DEFAULT_NODE_BATCH_SIZE = 50;
|
||||
private static final String DEFAULT_INITIAL_TRANSACTION_RANGE = "0-2000";
|
||||
|
||||
private int matadataTrackerParallelism;
|
||||
private int transactionDocsBatchSize;
|
||||
private int nodeBatchSize;
|
||||
private int maxNumberOfTransactions;
|
||||
|
||||
private ConcurrentLinkedQueue<Long> transactionsToReindex = new ConcurrentLinkedQueue<>();
|
||||
private ConcurrentLinkedQueue<Long> transactionsToIndex = new ConcurrentLinkedQueue<>();
|
||||
private ConcurrentLinkedQueue<Long> transactionsToPurge = new ConcurrentLinkedQueue<>();
|
||||
@@ -70,6 +84,22 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
private final boolean isRunningInProduction = !Boolean.parseBoolean(System.getProperty("alfresco.test", "false"));
|
||||
|
||||
private ForkJoinPool forkJoinPool;
|
||||
|
||||
// Share run and write locks across all MetadataTracker threads
|
||||
private static Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if nextTxCommitTimeService is available in the repository.
|
||||
* This service is used to find the next available transaction commit time from a given time,
|
||||
@@ -117,12 +147,22 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
InformationServer informationServer, boolean checkRepoServicesAvailability)
|
||||
{
|
||||
super(isMaster, p, client, coreName, informationServer, Tracker.Type.METADATA);
|
||||
transactionDocsBatchSize = Integer.parseInt(p.getProperty("alfresco.transactionDocsBatchSize", "100"));
|
||||
nodeBatchSize = Integer.parseInt(p.getProperty("alfresco.nodeBatchSize", "10"));
|
||||
threadHandler = new ThreadHandler(p, coreName, "MetadataTracker");
|
||||
|
||||
transactionDocsBatchSize = Integer.parseInt(p.getProperty("alfresco.transactionDocsBatchSize",
|
||||
String.valueOf(DEFAULT_TRANSACTION_DOCS_BATCH_SIZE)));
|
||||
nodeBatchSize = Integer.parseInt(p.getProperty("alfresco.nodeBatchSize",
|
||||
String.valueOf(DEFAULT_NODE_BATCH_SIZE)));
|
||||
maxNumberOfTransactions = Integer.parseInt(p.getProperty("alfresco.maxNumberOfTransactions", String.valueOf(DEFAULT_MAX_NUMBER_OF_TRANSACTIONS)));
|
||||
matadataTrackerParallelism = Integer.parseInt(p.getProperty("alfresco.metadataTrackerMaxParallelism",
|
||||
String.valueOf(DEFAULT_METADATA_TRACKER_MAX_PARALLELISM)));
|
||||
|
||||
String[] minTxninitialRangeString = p.getProperty("solr.initial.transaction.range", DEFAULT_INITIAL_TRANSACTION_RANGE).split("-");
|
||||
cascadeTrackerEnabled = informationServer.cascadeTrackingEnabled();
|
||||
String[] minTxninitialRangeString = p.getProperty("solr.initial.transaction.range", "0-2000").split("-");
|
||||
minTxnIdRange = new Pair<Long, Long>(Long.valueOf(minTxninitialRangeString[0]), Long.valueOf(minTxninitialRangeString[1]));
|
||||
minTxnIdRange = new Pair<>(Long.valueOf(minTxninitialRangeString[0]), Long.valueOf(minTxninitialRangeString[1]));
|
||||
forkJoinPool = new ForkJoinPool(matadataTrackerParallelism);
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
|
||||
// In order to apply performance optimizations, checking the availability of Repo Web Scripts is required.
|
||||
// As these services are available from ACS 6.2
|
||||
@@ -136,11 +176,11 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
log.warn("nextTxCommitTimeService is not available. Upgrade your ACS Repository version in order to use this feature: {} ", e.getMessage());
|
||||
LOGGER.warn("nextTxCommitTimeService is not available. Upgrade your ACS Repository version in order to use this feature: {} ", e.getMessage());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("Checking nextTxCommitTimeService failed.", e);
|
||||
LOGGER.error("Checking nextTxCommitTimeService failed.", e);
|
||||
}
|
||||
|
||||
// Try invoking txIntervalCommitTime service
|
||||
@@ -153,12 +193,12 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
log.warn("txIntervalCommitTimeServiceAvailable is not available. Upgrade your ACS Repository version " +
|
||||
LOGGER.warn("txIntervalCommitTimeServiceAvailable is not available. Upgrade your ACS Repository version " +
|
||||
"to use this feature with DB_ID_RANGE sharding: {} ", e.getMessage());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("Checking txIntervalCommitTimeServiceAvailable failed.", e);
|
||||
LOGGER.error("Checking txIntervalCommitTimeServiceAvailable failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +213,6 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
@Override
|
||||
protected void doTrack(String iterationId) throws AuthenticationException, IOException, JSONException, EncoderException
|
||||
{
|
||||
log.debug("### MetadataTracker doTrack ###");
|
||||
// MetadataTracker must wait until ModelTracker has run
|
||||
ModelTracker modelTracker = this.infoSrv.getAdminHandler().getTrackerRegistry().getModelTracker();
|
||||
if (modelTracker != null && modelTracker.hasModels())
|
||||
@@ -210,13 +249,11 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
private void trackRepository() throws IOException, AuthenticationException, JSONException, EncoderException
|
||||
{
|
||||
log.debug("####### MetadataTracker trackRepository Start #######");
|
||||
checkShutdown();
|
||||
|
||||
|
||||
// Check we are tracking the correct repository
|
||||
TrackerState state = super.getTrackerState();
|
||||
log.debug("####### MetadataTracker check CYCLE #######");
|
||||
log.debug(String.format("%s ### state: %s", coreName, state.toString()));
|
||||
if(state.getTrackerCycles() == 0)
|
||||
{
|
||||
//We have a new tracker state so do the checks.
|
||||
@@ -256,7 +293,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
{
|
||||
state.setCheckedLastTransactionTime(true);
|
||||
state.setCheckedFirstTransactionTime(true);
|
||||
log.info("No transactions found - no verification required");
|
||||
LOGGER.info("No transactions found - no verification required");
|
||||
|
||||
firstTransactions = client.getTransactions(null, minTxnIdRange.getFirst(), null, minTxnIdRange.getSecond(), 1);
|
||||
if (!firstTransactions.getTransactions().isEmpty())
|
||||
@@ -264,7 +301,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
Transaction firstTransaction = firstTransactions.getTransactions().get(0);
|
||||
long firstTransactionCommitTime = firstTransaction.getCommitTimeMs();
|
||||
state.setLastGoodTxCommitTimeInIndex(firstTransactionCommitTime);
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(firstTransactions, state);
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(firstTransactions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +325,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
log.warn("txIntervalCommitTimeServiceAvailable is not available. If you are using DB_ID_RANGE shard method, "
|
||||
LOGGER.warn("txIntervalCommitTimeServiceAvailable is not available. If you are using DB_ID_RANGE shard method, "
|
||||
+ "upgrade your ACS Repository version in order to use the skip transactions feature: {} ", e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -307,20 +344,20 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
if (setSize == 0)
|
||||
{
|
||||
log.error("First transaction was not found with the correct timestamp.");
|
||||
log.error("SOLR has successfully connected to your repository however the SOLR indexes and repository database do not match.");
|
||||
log.error("If this is a new or rebuilt database your SOLR indexes also need to be re-built to match the database.");
|
||||
log.error("You can also check your SOLR connection details in solrcore.properties.");
|
||||
LOGGER.error("First transaction was not found with the correct timestamp.");
|
||||
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 transaction not found with correct timestamp");
|
||||
}
|
||||
else if (setSize == 1)
|
||||
{
|
||||
state.setCheckedFirstTransactionTime(true);
|
||||
log.info("Verified first transaction and timestamp in index");
|
||||
LOGGER.info("Verified first transaction and timestamp in index");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Duplicate initial transaction found with correct timestamp");
|
||||
LOGGER.warn("Duplicate initial transaction found with correct timestamp");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +371,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
firstTransactions = client.getTransactions(null, minTxnIdRange.getFirst(), null, minTxnIdRange.getSecond(), 1);
|
||||
}
|
||||
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(firstTransactions, state);
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(firstTransactions);
|
||||
Long maxTxnCommitTimeInRepo = firstTransactions.getMaxTxnCommitTime();
|
||||
Long maxTxnIdInRepo = firstTransactions.getMaxTxnId();
|
||||
if (maxTxnCommitTimeInRepo != null && maxTxnIdInRepo != null)
|
||||
@@ -342,19 +379,19 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
Transaction maxTxInIndex = this.infoSrv.getMaxTransactionIdAndCommitTimeInIndex();
|
||||
if (maxTxInIndex.getCommitTimeMs() > maxTxnCommitTimeInRepo)
|
||||
{
|
||||
log.error("Last transaction was found in index with timestamp later than that of repository.");
|
||||
log.error("Max Tx In Index: " + maxTxInIndex.getId() + ", In Repo: " + maxTxnIdInRepo);
|
||||
log.error("Max Tx Commit Time In Index: " + maxTxInIndex.getCommitTimeMs() + ", In Repo: "
|
||||
LOGGER.error("Last transaction was found in index with timestamp later than that of repository.");
|
||||
LOGGER.error("Max Tx In Index: " + maxTxInIndex.getId() + ", In Repo: " + maxTxnIdInRepo);
|
||||
LOGGER.error("Max Tx Commit Time In Index: " + maxTxInIndex.getCommitTimeMs() + ", In Repo: "
|
||||
+ maxTxnCommitTimeInRepo);
|
||||
log.error("SOLR has successfully connected to your repository however the SOLR indexes and repository database do not match.");
|
||||
log.error("If this is a new or rebuilt database your SOLR indexes also need to be re-built to match the database.");
|
||||
log.error("You can also check your SOLR connection details in solrcore.properties.");
|
||||
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("Last transaction found in index with incorrect timestamp");
|
||||
}
|
||||
else
|
||||
{
|
||||
state.setCheckedLastTransactionTime(true);
|
||||
log.info("Verified last transaction timestamp in index less than or equal to that of repository.");
|
||||
LOGGER.info("Verified last transaction timestamp in index less than or equal to that of repository.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,9 +429,9 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
for (Node node : nodes)
|
||||
{
|
||||
docCount++;
|
||||
if (log.isDebugEnabled())
|
||||
if (LOGGER.isDebugEnabled())
|
||||
{
|
||||
log.debug(node.toString());
|
||||
LOGGER.debug(node.toString());
|
||||
}
|
||||
this.infoSrv.indexNode(node, false);
|
||||
checkShutdown();
|
||||
@@ -402,14 +439,15 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
// Index the transaction doc after the node - if this is not found then a reindex will be done.
|
||||
this.infoSrv.indexTransaction(info, false);
|
||||
log.info("INDEX ACTION - Transaction {} has been indexed", transactionId);
|
||||
LOGGER.info("INDEX ACTION - Transaction {} has been indexed", transactionId);
|
||||
requiresCommit = true;
|
||||
|
||||
trackerStats.addTxDocs(nodes.size());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("INDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId);
|
||||
LOGGER.info("INDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +456,6 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
if(this.infoSrv.getRegisteredSearcherCount() < getMaxLiveSearchers())
|
||||
{
|
||||
checkShutdown();
|
||||
//this.infoSrv.commit();
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedNodeTime(docCount, endElapsed-startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
@@ -450,7 +487,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
node.setTxnId(Long.MAX_VALUE);
|
||||
|
||||
this.infoSrv.indexNode(node, false);
|
||||
log.info("INDEX ACTION - Node {} has been reindexed", node.getId());
|
||||
LOGGER.info("INDEX ACTION - Node {} has been reindexed", node.getId());
|
||||
requiresCommit = true;
|
||||
}
|
||||
checkShutdown();
|
||||
@@ -491,9 +528,9 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
for (Node node : nodes)
|
||||
{
|
||||
docCount++;
|
||||
if (log.isDebugEnabled())
|
||||
if (LOGGER.isDebugEnabled())
|
||||
{
|
||||
log.debug(node.toString());
|
||||
LOGGER.debug(node.toString());
|
||||
}
|
||||
this.infoSrv.indexNode(node, true);
|
||||
checkShutdown();
|
||||
@@ -501,11 +538,11 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
// Index the transaction doc after the node - if this is not found then a reindex will be done.
|
||||
this.infoSrv.indexTransaction(info, true);
|
||||
log.info("REINDEX ACTION - Transaction {} has been reindexed", transactionId);
|
||||
LOGGER.info("REINDEX ACTION - Transaction {} has been reindexed", transactionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("REINDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId);
|
||||
LOGGER.info("REINDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +585,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
node.setTxnId(Long.MAX_VALUE);
|
||||
|
||||
this.infoSrv.indexNode(node, true);
|
||||
log.info("REINDEX ACTION - Node {} has been reindexed", node.getId());
|
||||
LOGGER.info("REINDEX ACTION - Node {} has been reindexed", node.getId());
|
||||
requiresCommit = true;
|
||||
}
|
||||
checkShutdown();
|
||||
@@ -570,7 +607,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
if (query != null)
|
||||
{
|
||||
this.infoSrv.reindexNodeByQuery(query);
|
||||
log.info("REINDEX ACTION - Nodes from query {} have been reindexed", query);
|
||||
LOGGER.info("REINDEX ACTION - Nodes from query {} have been reindexed", query);
|
||||
requiresCommit = true;
|
||||
}
|
||||
checkShutdown();
|
||||
@@ -595,7 +632,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
// make sure it is cleaned out so we do not miss deletes
|
||||
this.infoSrv.deleteByTransactionId(transactionId);
|
||||
requiresCommit = true;
|
||||
log.info("PURGE ACTION - Purged transactionId {}", transactionId);
|
||||
LOGGER.info("PURGE ACTION - Purged transactionId {}", transactionId);
|
||||
}
|
||||
checkShutdown();
|
||||
}
|
||||
@@ -603,7 +640,6 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
if(requiresCommit)
|
||||
{
|
||||
checkShutdown();
|
||||
//this.infoSrv.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,12 +652,30 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
{
|
||||
// make sure it is cleaned out so we do not miss deletes
|
||||
this.infoSrv.deleteByNodeId(nodeId);
|
||||
log.info("PURGE ACTION - Purged nodeId {}", nodeId);
|
||||
LOGGER.info("PURGE ACTION - Purged nodeId {}", nodeId);
|
||||
}
|
||||
checkShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The fromCommitTime tells getSomeTransactions() where to start, this actually fairly straight forward.
|
||||
*
|
||||
* What makes this code so tricky to understand is the state.getTimeToStopIndexing().
|
||||
*
|
||||
* There are two scenarios to keep in mind:
|
||||
*
|
||||
* 1) Full re-index: In this scenario the state.getTimeToStopIndexing() will never stop the indexing.
|
||||
*
|
||||
* 2) Up-to-date indexing: This is where state.getTimeToStopIndexing() gets interesting. In this scenario
|
||||
* the Solr index is already up to date with the repo and it is tracking new transactions. The state.getTimeToStopIndexing()
|
||||
* in this scenario causes the getSomeTransactions() call to stop returning results if it finds a transaction
|
||||
* beyond a specific point in time. This will break out of this loop and end the tracker run.
|
||||
*
|
||||
* The next time the metadata tracker runs the "continueState()" method applies the "hole retention"
|
||||
* to state.getLastGoodTxCommitTimeInIndex(). This causes the state.getLastGoodTxCommitTimeInIndex() to scan
|
||||
* for prior transactions that might have been missed.
|
||||
*/
|
||||
protected Long getTxFromCommitTime(BoundedDeque<Transaction> txnsFound, long lastGoodTxCommitTimeInIndex) {
|
||||
if (txnsFound.size() > 0)
|
||||
{
|
||||
@@ -664,10 +718,6 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
// step forward in time until we find something or hit the time bound
|
||||
// max id unbounded
|
||||
Long startTime = fromCommitTime == null ? 0L : fromCommitTime;
|
||||
log.debug(String.format("#### %s MetadataTracker getSomeTransactions start time: %d end: %d",
|
||||
this.coreName,
|
||||
startTime,
|
||||
endTime));
|
||||
if(startTime == 0)
|
||||
{
|
||||
return client.getTransactions(startTime,
|
||||
@@ -677,6 +727,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
maxResults,
|
||||
shardstate);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
transactions = client.getTransactions(startTime, null, startTime + actualTimeStep, null, maxResults, shardstate);
|
||||
@@ -688,7 +739,8 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
Long nextTxCommitTime = client.getNextTxCommitTime(coreName, startTime);
|
||||
if (nextTxCommitTime != -1)
|
||||
{
|
||||
log.info("Advancing transactions from {} to {}", startTime, nextTxCommitTime);
|
||||
LOGGER.info("{}-[CORE {}] Advancing transactions from {} to {}",
|
||||
Thread.currentThread().getId(), coreName, startTime, nextTxCommitTime);
|
||||
transactions = client.getTransactions(nextTxCommitTime, null, nextTxCommitTime + actualTimeStep, null, maxResults, shardstate);
|
||||
}
|
||||
}
|
||||
@@ -700,26 +752,154 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
return transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* When using DB_ID_RANGE, fromCommitTime cannot be before the commit time of the first transaction
|
||||
* for the DB_ID_RANGE to be indexed and commit time of the last transaction cannot be lower than fromCommitTime.
|
||||
* When there isn't nodes in that range, -1 is returned as commit times
|
||||
*
|
||||
* @param fromCommitTime Starting commit time to get transactions from Repository
|
||||
* @param txnsFound List of transactions previously found
|
||||
* @return List of transactions to be indexed
|
||||
*/
|
||||
private Transactions getDBIDRangeTransactions(Long fromCommitTime, BoundedDeque<Transaction> txnsFound)
|
||||
throws NoSuchMethodException, AuthenticationException, IOException, JSONException, EncoderException
|
||||
{
|
||||
boolean shardOutOfRange = false;
|
||||
|
||||
DBIDRangeRouter dbIdRangeRouter = (DBIDRangeRouter) docRouter;
|
||||
Pair<Long, Long> commitTimes = client.getTxIntervalCommitTime(coreName,
|
||||
dbIdRangeRouter.getStartRange(), dbIdRangeRouter.getEndRange());
|
||||
Long shardMinCommitTime = commitTimes.getFirst();
|
||||
Long shardMaxCommitTime = commitTimes.getSecond();
|
||||
|
||||
// Node Range it's not still available in repository
|
||||
if (shardMinCommitTime == -1)
|
||||
{
|
||||
LOGGER.debug(
|
||||
"{}-[CORE {}] [DB_ID_RANGE] No nodes in range [{}-{}] "
|
||||
+ "exist in the repository. Indexing only latest transaction.",
|
||||
Thread.currentThread().getId(), coreName, dbIdRangeRouter.getStartRange(),
|
||||
dbIdRangeRouter.getEndRange());
|
||||
shardOutOfRange = true;
|
||||
}
|
||||
if (fromCommitTime > shardMaxCommitTime)
|
||||
{
|
||||
LOGGER.debug(
|
||||
"{}-[CORE {}] [DB_ID_RANGE] Last commit time is greater that max commit time in in range [{}-{}]. "
|
||||
+ "Indexing only latest transaction.",
|
||||
Thread.currentThread().getId(), coreName, dbIdRangeRouter.getStartRange(),
|
||||
dbIdRangeRouter.getEndRange());
|
||||
shardOutOfRange = true;
|
||||
}
|
||||
// Initial commit time for Node Range is greater than calculated from commit time
|
||||
if (fromCommitTime < shardMinCommitTime)
|
||||
{
|
||||
LOGGER.debug("{}-[CORE {}] [DB_ID_RANGE] Skipping transactions from {} to {}",
|
||||
Thread.currentThread().getId(), coreName, fromCommitTime, shardMinCommitTime);
|
||||
fromCommitTime = shardMinCommitTime;
|
||||
}
|
||||
|
||||
Transactions transactions = getSomeTransactions(txnsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, maxNumberOfTransactions,
|
||||
state.getTimeToStopIndexing());
|
||||
|
||||
|
||||
// When transactions are out of Shard range, only the latest transaction needs to be indexed
|
||||
// in order to preserve the state up-to-date of the MetadataTracker
|
||||
if (shardOutOfRange)
|
||||
{
|
||||
Transaction latestTransaction = new Transaction();
|
||||
latestTransaction.setCommitTimeMs(transactions.getMaxTxnCommitTime());
|
||||
latestTransaction.setId(transactions.getMaxTxnId());
|
||||
transactions = new Transactions(
|
||||
Arrays.asList(latestTransaction),
|
||||
transactions.getMaxTxnCommitTime(),
|
||||
transactions.getMaxTxnId());
|
||||
}
|
||||
|
||||
return transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove transactions already present in SOLR index
|
||||
*
|
||||
* @param transactions List of transactions to be indexed
|
||||
* @return List of transactions not indexed in SOLR index
|
||||
*/
|
||||
private Transactions removeIndexedTransactions(Transactions transactions)
|
||||
{
|
||||
return new Transactions(transactions.getTransactions().stream()
|
||||
.filter(transaction -> {
|
||||
try
|
||||
{
|
||||
boolean isInIndex = (transaction.getCommitTimeMs() <= state.getLastIndexedTxCommitTime() &&
|
||||
infoSrv.txnInIndex(transaction.getId(), true));
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Skipping Transaction Id {} as it was already indexed",
|
||||
Thread.currentThread().getId(), coreName, transaction.getId());
|
||||
}
|
||||
return !isInIndex;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn(
|
||||
"{}-[CORE {}] Error catched while checking if Transaction Id {} was in index",
|
||||
Thread.currentThread().getId(), coreName, transaction.getId(), e);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
//fixme remove
|
||||
/**
|
||||
* Keep only transactions previous to node transaction Id
|
||||
*
|
||||
* @param transactions List of transactions from Repository
|
||||
* @param node Last Node indexed in the cycle
|
||||
* @return Filtered list of transactions
|
||||
*/
|
||||
private Transactions filterTransactionsByNode(Transactions transactions, Node node)
|
||||
{
|
||||
return new Transactions(transactions.getTransactions().stream()
|
||||
.filter(transaction -> {
|
||||
return transaction.getId() < node.getTxnId();
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indexing new transactions from repository in batches of "transactionDocsBatchSize" size.
|
||||
*
|
||||
* Additionally, the nodes inside a transaction batch are indexed in batches of "nodeBatchSize" size.
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
* @throws EncoderException
|
||||
*/
|
||||
protected void trackTransactions() throws AuthenticationException, IOException, JSONException, EncoderException
|
||||
{
|
||||
long startElapsed = System.nanoTime();
|
||||
|
||||
|
||||
boolean upToDate = false;
|
||||
Transactions transactions;
|
||||
BoundedDeque<Transaction> txnsFound = new BoundedDeque<Transaction>(100);
|
||||
HashSet<Transaction> txsIndexed = new LinkedHashSet<>();
|
||||
long totalUpdatedDocs = 0;
|
||||
int docCount = 0;
|
||||
|
||||
int totalUpdatedDocs = 0;
|
||||
|
||||
LOGGER.info("{}-[CORE {}] Starting metadata tracker execution", Thread.currentThread().getId(), coreName);
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
/*
|
||||
* This write lock is used to lock out the Commit Tracker. The ensures that the MetaDataTracker will
|
||||
* not be indexing content while commits or rollbacks are occurring.
|
||||
*/
|
||||
|
||||
getWriteLock().acquire();
|
||||
|
||||
/*
|
||||
@@ -727,184 +907,94 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
* tracker state could have been invalidated due to a rollback by the CommitTracker.
|
||||
* In this case the state will revert to the last transaction state record in the index.
|
||||
*/
|
||||
|
||||
this.state = getTrackerState();
|
||||
|
||||
|
||||
/*
|
||||
* The fromCommitTime tells getSomeTransactions() where to start, this actually fairly straight forward.
|
||||
*
|
||||
* What makes this code so tricky to understand is the state.getTimeToStopIndexing().
|
||||
*
|
||||
* There are two scenarios to keep in mind:
|
||||
*
|
||||
* 1) Full re-index: In this scenario the state.getTimeToStopIndexing() will never stop the indexing.
|
||||
*
|
||||
* 2) Up-to-date indexing: This is where state.getTimeToStopIndexing() gets interesting. In this scenario
|
||||
* the Solr index is already up to date with the repo and it is tracking new transactions. The state.getTimeToStopIndexing()
|
||||
* in this scenario causes the getSomeTransactions() call to stop returning results if it finds a transaction
|
||||
* beyond a specific point in time. This will break out of this loop and end the tracker run.
|
||||
*
|
||||
* The next time the metadata tracker runs the "continueState()" method applies the "hole retention"
|
||||
* to state.getLastGoodTxCommitTimeInIndex(). This causes the state.getLastGoodTxCommitTimeInIndex() to scan
|
||||
* for prior transactions that might have been missed.
|
||||
*
|
||||
*/
|
||||
|
||||
Long fromCommitTime = getTxFromCommitTime(txnsFound,
|
||||
Long fromCommitTime = getTxFromCommitTime(txnsFound,
|
||||
state.getLastIndexedTxCommitTime() == 0 ? state.getLastGoodTxCommitTimeInIndex() : state.getLastIndexedTxCommitTime());
|
||||
log.debug("#### Check txnsFound : " + txnsFound.size());
|
||||
log.debug("======= fromCommitTime: " + fromCommitTime);
|
||||
|
||||
// When using DB_ID_RANGE, fromCommitTime cannot be before the commit time of the first transaction
|
||||
// for the DB_ID_RANGE to be indexed and commit time of the last transaction cannot be lower than fromCommitTime.
|
||||
// When there isn't nodes in that range, -1 is returned as commit times
|
||||
boolean shardOutOfRange = false;
|
||||
|
||||
// Get transaction list to be indexed
|
||||
if (docRouter instanceof DBIDRangeRouter && txIntervalCommitTimeServiceAvailable)
|
||||
{
|
||||
|
||||
DBIDRangeRouter dbIdRangeRouter = (DBIDRangeRouter) docRouter;
|
||||
Pair<Long, Long> commitTimes = client.getTxIntervalCommitTime(coreName,
|
||||
dbIdRangeRouter.getStartRange(), dbIdRangeRouter.getEndRange());
|
||||
Long shardMinCommitTime = commitTimes.getFirst();
|
||||
Long shardMaxCommitTime = commitTimes.getSecond();
|
||||
|
||||
// Node Range it's not still available in repository
|
||||
if (shardMinCommitTime == -1)
|
||||
{
|
||||
log.debug(
|
||||
"#### [DB_ID_RANGE] No nodes in range [{}-{}] "
|
||||
+ "exist in the repository. Indexing only latest transaction.",
|
||||
dbIdRangeRouter.getStartRange(), dbIdRangeRouter.getEndRange());
|
||||
shardOutOfRange = true;
|
||||
}
|
||||
if (fromCommitTime > shardMaxCommitTime)
|
||||
{
|
||||
log.debug("#### [DB_ID_RANGE] Last commit time is greater that max commit time in in range [{}-{}]. "
|
||||
+ "Indexing only latest transaction.",
|
||||
dbIdRangeRouter.getStartRange(), dbIdRangeRouter.getEndRange());
|
||||
shardOutOfRange = true;
|
||||
}
|
||||
// Initial commit time for Node Range is greater than calculated from commit time
|
||||
if (fromCommitTime < shardMinCommitTime)
|
||||
{
|
||||
log.debug("#### [DB_ID_RANGE] Skipping transactions from {} to {}",
|
||||
fromCommitTime, shardMinCommitTime);
|
||||
fromCommitTime = shardMinCommitTime;
|
||||
}
|
||||
transactions = getDBIDRangeTransactions(fromCommitTime, txnsFound);
|
||||
}
|
||||
|
||||
log.debug("#### Get txn from commit time: " + fromCommitTime);
|
||||
transactions = getSomeTransactions(txnsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, 2000,
|
||||
state.getTimeToStopIndexing());
|
||||
|
||||
|
||||
// When transactions are out of Shard range, only the latest transaction needs to be indexed
|
||||
// in order to preserve the state up-to-date of the MetadataTracker
|
||||
if (shardOutOfRange)
|
||||
else
|
||||
{
|
||||
Transaction latestTransaction = new Transaction();
|
||||
latestTransaction.setCommitTimeMs(transactions.getMaxTxnCommitTime());
|
||||
latestTransaction.setId(transactions.getMaxTxnId());
|
||||
transactions = new Transactions(
|
||||
Arrays.asList(latestTransaction),
|
||||
transactions.getMaxTxnCommitTime(),
|
||||
transactions.getMaxTxnId());
|
||||
log.debug("#### [DB_ID_RANGE] Latest transaction to be indexed {}", latestTransaction);
|
||||
transactions = getSomeTransactions(txnsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, maxNumberOfTransactions,
|
||||
state.getTimeToStopIndexing());
|
||||
}
|
||||
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(transactions, state);
|
||||
// Remove transactions already indexed
|
||||
transactions = removeIndexedTransactions(transactions);
|
||||
|
||||
log.debug("Scanning transactions ...");
|
||||
if (transactions.getTransactions().size() > 0) {
|
||||
log.info(".... from " + transactions.getTransactions().get(0));
|
||||
log.info(".... to " + transactions.getTransactions().get(transactions.getTransactions().size() - 1));
|
||||
} else {
|
||||
log.info(".... none found after lastTxCommitTime "
|
||||
+ ((txnsFound.size() > 0) ? txnsFound.getLast().getCommitTimeMs() : state
|
||||
.getLastIndexedTxCommitTime()));
|
||||
if (transactions.getTransactions().size() > 0)
|
||||
{
|
||||
LOGGER.info("{}-[CORE {}] Found {} transactions after lastTxCommitTime {}, transactions from {} to {}",
|
||||
Thread.currentThread().getId(),
|
||||
coreName,
|
||||
transactions.getTransactions().size(),
|
||||
fromCommitTime,
|
||||
transactions.getTransactions().get(0),
|
||||
transactions.getTransactions().get(transactions.getTransactions().size() - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("{}-[CORE {}] No transaction found after lastTxCommitTime {}",
|
||||
Thread.currentThread().getId(),
|
||||
coreName,
|
||||
((txnsFound.size() > 0) ? txnsFound.getLast().getCommitTimeMs() : state.getLastIndexedTxCommitTime()));
|
||||
}
|
||||
|
||||
ArrayList<Transaction> txBatch = new ArrayList<>();
|
||||
// Group the transactions in batches of transactionDocsBatchSize (or less)
|
||||
List<List<Transaction>> txBatches = new ArrayList<>();
|
||||
List<Transaction> txBatch = new ArrayList<>();
|
||||
for (Transaction info : transactions.getTransactions()) {
|
||||
|
||||
/*
|
||||
* isInIndex is used to ensure transactions that are being re-pulled due to "hole retention" are not re-indexed if
|
||||
* they have already been indexed.
|
||||
*
|
||||
* The logic in infoSrv.txnInIndex() first checks an in-memory LRUcache for the txnId. If it doesn't find it in the cache
|
||||
* it checks the index. The LRUCache is only needed for txnId's that have been indexed but are not yet visible in the index for
|
||||
* one of two reasons:
|
||||
*
|
||||
* 1) The commit tracker has not yet committed the transaction.
|
||||
* 2) The txnId has been committed to the index but the new searcher has not yet been warmed.
|
||||
*
|
||||
* This means that to ensure txnId's are not needlessly reprocessed during hole retention, the LRUCache must be large
|
||||
* enough to cover the time between when a txnId is indexed and when it becomes visible.
|
||||
*/
|
||||
|
||||
boolean isInIndex = (infoSrv.txnInIndex(info.getId(), true) && info.getCommitTimeMs() <= state.getLastIndexedTxCommitTime());
|
||||
if (isInIndex) {
|
||||
txnsFound.add(info);
|
||||
} else {
|
||||
// Make sure we do not go ahead of where we started - we will check the holes here
|
||||
// correctly next time
|
||||
if (info.getCommitTimeMs() > state.getTimeToStopIndexing()) {
|
||||
upToDate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
txBatch.add(info);
|
||||
if (getUpdateAndDeleteCount(txBatch) > this.transactionDocsBatchSize) {
|
||||
|
||||
docCount += indexBatchOfTransactions(txBatch);
|
||||
totalUpdatedDocs += docCount;
|
||||
|
||||
for (Transaction scheduledTx : txBatch) {
|
||||
txnsFound.add(scheduledTx);
|
||||
txsIndexed.add(scheduledTx);
|
||||
}
|
||||
txBatch.clear();
|
||||
}
|
||||
if (LOGGER.isTraceEnabled())
|
||||
{
|
||||
LOGGER.trace("{}-[CORE {}] Tracking {} Transactions. Current Transaction Id to be indexed: {}",
|
||||
Thread.currentThread().getId(), coreName, transactions.getTransactions().size(), info.getId());
|
||||
}
|
||||
|
||||
if (docCount > batchCount) {
|
||||
indexTransactionsAfterAsynchronous(txsIndexed, state);
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedNodeTime(docCount, endElapsed - startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
docCount = 0;
|
||||
|
||||
//Release the write lock allowing the commit tracker to run.
|
||||
this.getWriteLock().release();
|
||||
//Re-acquire the write lock and keep indexing.
|
||||
this.getWriteLock().acquire();
|
||||
// Make sure we do not go ahead of where we started - we will check the holes here
|
||||
// correctly next time
|
||||
if (info.getCommitTimeMs() > state.getTimeToStopIndexing()) {
|
||||
upToDate = true;
|
||||
break;
|
||||
}
|
||||
checkShutdown();
|
||||
|
||||
txBatch.add(info);
|
||||
if (getUpdateAndDeleteCount(txBatch) > transactionDocsBatchSize) {
|
||||
txBatches.add(txBatch);
|
||||
txBatch = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
if (!txBatch.isEmpty())
|
||||
{
|
||||
txBatches.add(txBatch);
|
||||
}
|
||||
|
||||
// Index any remaining transactions bringing the index to a consistent state so the CommitTracker can commit if need be.
|
||||
// Index batches of transactions and the nodes updated or deleted within the transaction
|
||||
for (List<Transaction> batch : txBatches)
|
||||
{
|
||||
|
||||
if (!txBatch.isEmpty()) {
|
||||
if (this.getUpdateAndDeleteCount(txBatch) > 0) {
|
||||
docCount += indexBatchOfTransactions(txBatch);
|
||||
totalUpdatedDocs += docCount;
|
||||
}
|
||||
// Index nodes contained in the transactions
|
||||
int docCount = indexBatchOfTransactions(batch, totalUpdatedDocs);
|
||||
totalUpdatedDocs += docCount;
|
||||
|
||||
for (Transaction scheduledTx : txBatch) {
|
||||
txnsFound.add(scheduledTx);
|
||||
txsIndexed.add(scheduledTx);
|
||||
}
|
||||
txBatch.clear();
|
||||
}
|
||||
// Add the transactions as found to avoid processing them again in the next iteration
|
||||
batch.forEach(transaction -> txnsFound.add(transaction));
|
||||
|
||||
if (txsIndexed.size() > 0) {
|
||||
indexTransactionsAfterAsynchronous(txsIndexed, state);
|
||||
// Index the transactions
|
||||
indexTransactionsAfterWorker(batch);
|
||||
long endElapsed = System.nanoTime();
|
||||
trackerStats.addElapsedNodeTime(docCount, endElapsed - startElapsed);
|
||||
startElapsed = endElapsed;
|
||||
docCount = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
setLastTxCommitTimeAndTxIdInTrackerState(transactions);
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -918,10 +1008,14 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
while ((transactions.getTransactions().size() > 0) && (upToDate == false));
|
||||
|
||||
log.debug("total number of docs with metadata updated: " + totalUpdatedDocs);
|
||||
LOGGER.info("{}-[CORE {}] Tracked {} DOCs", Thread.currentThread().getId(), coreName, totalUpdatedDocs);
|
||||
}
|
||||
|
||||
private void setLastTxCommitTimeAndTxIdInTrackerState(Transactions transactions, TrackerState state)
|
||||
/**
|
||||
* Update latest transaction indexed in MetadataTracker state
|
||||
* @param transactions List of transactions indexed
|
||||
*/
|
||||
private void setLastTxCommitTimeAndTxIdInTrackerState(Transactions transactions)
|
||||
{
|
||||
Long maxTxnCommitTime = transactions.getMaxTxnCommitTime();
|
||||
if (maxTxnCommitTime != null)
|
||||
@@ -936,13 +1030,17 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
private void indexTransactionsAfterAsynchronous(HashSet<Transaction> txsIndexed, TrackerState state)
|
||||
/**
|
||||
* Index transactions and update state of the tracker
|
||||
* @param txsIndexed List of transactions to be indexed
|
||||
* @throws IOException
|
||||
*/
|
||||
private void indexTransactionsAfterWorker(List<Transaction> txsIndexed)
|
||||
throws IOException
|
||||
{
|
||||
waitForAsynchronous();
|
||||
for (Transaction tx : txsIndexed)
|
||||
{
|
||||
super.infoSrv.indexTransaction(tx, true);
|
||||
infoSrv.indexTransaction(tx, true);
|
||||
// Transactions are ordered by commit time and tie-broken by tx id
|
||||
if (tx.getCommitTimeMs() > state.getLastIndexedTxCommitTime()
|
||||
|| tx.getCommitTimeMs() == state.getLastIndexedTxCommitTime()
|
||||
@@ -954,9 +1052,13 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
trackerStats.addTxDocs((int) (tx.getDeletes() + tx.getUpdates()));
|
||||
}
|
||||
txsIndexed.clear();
|
||||
//super.infoSrv.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of updated and deleted nodes in a list of transactions
|
||||
* @param txs List of transactions
|
||||
* @return Number of updated and deleted nodes
|
||||
*/
|
||||
private long getUpdateAndDeleteCount(List<Transaction> txs)
|
||||
{
|
||||
long count = 0;
|
||||
@@ -967,12 +1069,25 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
return count;
|
||||
}
|
||||
|
||||
private int indexBatchOfTransactions(List<Transaction> txBatch) throws AuthenticationException, IOException, JSONException
|
||||
{
|
||||
int nodeCount = 0;
|
||||
List<Transaction> nonEmptyTxs = new ArrayList<>(txBatch.size());
|
||||
GetNodesParameters gnp = new GetNodesParameters();
|
||||
List<Long> txIds = new ArrayList<>();
|
||||
/**
|
||||
* Index a batch of transactions.
|
||||
*
|
||||
* Updated or deleted nodes from these transactions are also packed into batches in order to get
|
||||
* the metadata of the nodes in smaller invocations to Repository
|
||||
*
|
||||
* @param txBatch Batch of transactions to be indexed
|
||||
* @param indexedNodes Number of nodes indexed in this Tracker execution
|
||||
*
|
||||
* @return Number of nodes indexed and last node indexed
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private int indexBatchOfTransactions(List<Transaction> txBatch, int indexedNodes) throws AuthenticationException, IOException, JSONException, ExecutionException, InterruptedException {
|
||||
// Skip transactions without modifications (updates, deletes)
|
||||
ArrayList<Transaction> nonEmptyTxs = new ArrayList<>(txBatch.size());
|
||||
ArrayList<Long> txIds = new ArrayList<>();
|
||||
for (Transaction tx : txBatch)
|
||||
{
|
||||
if (tx.getUpdates() > 0 || tx.getDeletes() > 0)
|
||||
@@ -982,6 +1097,8 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
}
|
||||
}
|
||||
|
||||
// Get Nodes Id properties for every transaction
|
||||
GetNodesParameters gnp = new GetNodesParameters();
|
||||
gnp.setTransactionIds(txIds);
|
||||
gnp.setStoreProtocol(storeRef.getProtocol());
|
||||
gnp.setStoreIdentifier(storeRef.getIdentifier());
|
||||
@@ -990,42 +1107,38 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
|
||||
gnp.setCoreName(coreName);
|
||||
List<Node> nodes = client.getNodes(gnp, Integer.MAX_VALUE);
|
||||
|
||||
ArrayList<Node> nodeBatch = new ArrayList<>();
|
||||
for (Node node : nodes)
|
||||
|
||||
if (LOGGER.isDebugEnabled())
|
||||
{
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug(node.toString());
|
||||
}
|
||||
nodeBatch.add(node);
|
||||
if (nodeBatch.size() > nodeBatchSize)
|
||||
{
|
||||
nodeCount += nodeBatch.size();
|
||||
NodeIndexWorkerRunnable niwr = new NodeIndexWorkerRunnable(this.threadHandler, nodeBatch, this.infoSrv);
|
||||
this.threadHandler.scheduleTask(niwr);
|
||||
nodeBatch = new ArrayList<>();
|
||||
}
|
||||
LOGGER.debug("{}-[CORE {}] Found {} Nodes to be indexed from Transactions: {}", Thread.currentThread().getId(),
|
||||
coreName, nodes.size(), txIds);
|
||||
}
|
||||
|
||||
if (nodeBatch.size() > 0)
|
||||
{
|
||||
nodeCount += nodeBatch.size();
|
||||
NodeIndexWorkerRunnable niwr = new NodeIndexWorkerRunnable(this.threadHandler, nodeBatch, this.infoSrv);
|
||||
this.threadHandler.scheduleTask(niwr);
|
||||
nodeBatch = new ArrayList<>();
|
||||
}
|
||||
return nodeCount;
|
||||
|
||||
// Group the nodes in batches of nodeBatchSize (or less)
|
||||
List<List<Node>> nodeBatches = Lists.partition(nodes, nodeBatchSize);
|
||||
|
||||
Integer processedNodes = forkJoinPool.submit(() ->
|
||||
nodeBatches.parallelStream().map(batch -> {
|
||||
new NodeIndexWorker(batch, infoSrv).run();
|
||||
return batch.size();
|
||||
}).reduce(0, Integer::sum)).get();
|
||||
|
||||
|
||||
|
||||
return processedNodes;
|
||||
}
|
||||
|
||||
class NodeIndexWorkerRunnable extends AbstractWorkerRunnable
|
||||
|
||||
/**
|
||||
* Node Indexing class running synchronously all the tracking operations.
|
||||
*/
|
||||
class NodeIndexWorker extends AbstractWorker
|
||||
{
|
||||
InformationServer infoServer;
|
||||
List<Node> nodes;
|
||||
|
||||
NodeIndexWorkerRunnable(QueueHandler queueHandler, List<Node> nodes, InformationServer infoServer)
|
||||
NodeIndexWorker(List<Node> nodes, InformationServer infoServer)
|
||||
{
|
||||
super(queueHandler);
|
||||
this.infoServer = infoServer;
|
||||
this.nodes = nodes;
|
||||
}
|
||||
@@ -1187,7 +1300,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker
|
||||
long endTime = System.currentTimeMillis() + infoSrv.getHoleRetention();
|
||||
DO: do
|
||||
{
|
||||
transactions = getSomeTransactions(txnsFound, lastTxCommitTime, TIME_STEP_1_HR_IN_MS, 2000, endTime);
|
||||
transactions = getSomeTransactions(txnsFound, lastTxCommitTime, TIME_STEP_1_HR_IN_MS, maxNumberOfTransactions, endTime);
|
||||
for (Transaction info : transactions.getTransactions())
|
||||
{
|
||||
// include
|
||||
|
||||
+26
-4
@@ -30,6 +30,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
@@ -47,6 +49,8 @@ import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.alfresco.solr.config.ConfigUtil;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.json.JSONException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @startuml
|
||||
@@ -82,6 +86,7 @@ import org.json.JSONException;
|
||||
*/
|
||||
public class ModelTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModelTracker.class);
|
||||
|
||||
private final Set<StoreRef> indexedStores = new HashSet<>();
|
||||
private final Set<StoreRef> ignoredStores = new HashSet<>();
|
||||
@@ -100,19 +105,36 @@ public class ModelTracker extends AbstractTracker implements Tracker
|
||||
private volatile boolean hasModels = false;
|
||||
private File alfrescoModelDir;
|
||||
|
||||
// Share run and write locks across all ModelTracker threads
|
||||
private static Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
public ModelTracker(String solrHome, Properties p, SOLRAPIClient client, String coreName,
|
||||
InformationServer informationServer)
|
||||
{
|
||||
super(p, client, coreName, informationServer, Tracker.Type.MODEL);
|
||||
String normalSolrHome = SolrResourceLoader.normalizeDir(solrHome);
|
||||
alfrescoModelDir = new File(ConfigUtil.locateProperty("solr.model.dir", normalSolrHome+"alfrescoModels"));
|
||||
logger.info("Alfresco Model dir " + alfrescoModelDir);
|
||||
LOGGER.info("Alfresco Model dir {}", alfrescoModelDir);
|
||||
if (!alfrescoModelDir.exists())
|
||||
{
|
||||
alfrescoModelDir.mkdir();
|
||||
}
|
||||
|
||||
loadPersistedModels();
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,7 +219,7 @@ public class ModelTracker extends AbstractTracker implements Tracker
|
||||
int registeredSearcherCount = this.infoSrv.getRegisteredSearcherCount();
|
||||
if (registeredSearcherCount >= getMaxLiveSearchers())
|
||||
{
|
||||
logger.info(".... skipping tracking registered searcher count = " + registeredSearcherCount);
|
||||
LOGGER.info(".... skipping tracking registered searcher count = {}", registeredSearcherCount);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -268,7 +290,7 @@ public class ModelTracker extends AbstractTracker implements Tracker
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
logger.error("Model tracking failed for core: "+ coreName, t);
|
||||
LOGGER.error("Model tracking failed for core: {}", coreName, t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -534,7 +556,7 @@ public class ModelTracker extends AbstractTracker implements Tracker
|
||||
{
|
||||
loadedModels.add(modelName);
|
||||
}
|
||||
logger.info("Loading model " + model.getName());
|
||||
LOGGER.info("Loading model {}", model.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-1
@@ -26,9 +26,14 @@ import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.TrackerState;
|
||||
import org.alfresco.solr.client.SOLRAPIClient;
|
||||
import org.apache.commons.codec.EncoderException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* Despite belonging to the Tracker ecosystem, this component is actually a publisher, which periodically informs
|
||||
@@ -47,6 +52,25 @@ import java.util.Properties;
|
||||
*/
|
||||
public class SlaveCoreStatePublisher extends CoreStatePublisher
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SlaveCoreStatePublisher.class);
|
||||
|
||||
|
||||
// Share run and write locks across all SlaveCoreStatePublisher threads
|
||||
private static final Map<String, Semaphore> RUN_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Semaphore> WRITE_LOCK_BY_CORE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Semaphore getWriteLock()
|
||||
{
|
||||
return WRITE_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Semaphore getRunLock()
|
||||
{
|
||||
return RUN_LOCK_BY_CORE.get(coreName);
|
||||
}
|
||||
|
||||
public SlaveCoreStatePublisher(
|
||||
boolean isMaster,
|
||||
Properties coreProperties,
|
||||
@@ -55,6 +79,9 @@ public class SlaveCoreStatePublisher extends CoreStatePublisher
|
||||
SolrInformationServer informationServer)
|
||||
{
|
||||
super(isMaster, coreProperties, repositoryClient, name, informationServer, NODE_STATE_PUBLISHER);
|
||||
|
||||
RUN_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
WRITE_LOCK_BY_CORE.put(coreName, new Semaphore(1, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,7 +94,7 @@ public class SlaveCoreStatePublisher extends CoreStatePublisher
|
||||
}
|
||||
catch (EncoderException | IOException | AuthenticationException exception )
|
||||
{
|
||||
logger.error("Unable to publish this node state. " +
|
||||
LOGGER.error("Unable to publish this node state. " +
|
||||
"A failure condition has been met during the outbound subscription message encoding process. " +
|
||||
"See the stacktrace below for further details.", exception);
|
||||
}
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.alfresco.solr.tracker.pool.DefaultTrackerPoolFactory;
|
||||
import org.alfresco.solr.tracker.pool.TrackerPoolFactory;
|
||||
|
||||
/**
|
||||
* This class handles threads for trackers.
|
||||
*
|
||||
* @author Ahmed Owian
|
||||
*/
|
||||
public class ThreadHandler implements QueueHandler
|
||||
{
|
||||
/** the instance that will be given out by the factory */
|
||||
private ThreadPoolExecutor threadPool;
|
||||
private LinkedBlockingQueue<AbstractWorkerRunnable> threadQueue = new LinkedBlockingQueue<>();
|
||||
private ReentrantReadWriteLock threadLock = new ReentrantReadWriteLock(true);
|
||||
|
||||
public ThreadHandler(Properties p, String coreName, String trackerName)
|
||||
{
|
||||
// construct the instance
|
||||
TrackerPoolFactory trackerPoolFactory = new DefaultTrackerPoolFactory(p, coreName, trackerName);
|
||||
threadPool = trackerPoolFactory.create();
|
||||
}
|
||||
|
||||
|
||||
public void scheduleTask(AbstractWorkerRunnable awr)
|
||||
{
|
||||
try
|
||||
{
|
||||
threadLock.writeLock().lock();
|
||||
// Add the runnable to the queue to ensure ordering
|
||||
threadQueue.add(awr);
|
||||
}
|
||||
finally
|
||||
{
|
||||
threadLock.writeLock().unlock();
|
||||
}
|
||||
threadPool.execute(awr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the job from the queue and notifies the HEAD
|
||||
*/
|
||||
public void removeFromQueueAndProdHead(AbstractWorkerRunnable job)
|
||||
{
|
||||
try
|
||||
{
|
||||
threadLock.writeLock().lock();
|
||||
// Remove self from head of queue
|
||||
threadQueue.remove(job);
|
||||
}
|
||||
finally
|
||||
{
|
||||
threadLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-safe method to peek at the head of the queue
|
||||
*/
|
||||
public AbstractWorkerRunnable peekHeadReindexWorker()
|
||||
{
|
||||
try
|
||||
{
|
||||
threadLock.readLock().lock();
|
||||
return threadQueue.peek();
|
||||
}
|
||||
finally
|
||||
{
|
||||
threadLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDownThreadPool()
|
||||
{
|
||||
if (threadPool != null)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
@@ -28,8 +29,12 @@ import org.slf4j.LoggerFactory;
|
||||
* Generic Solr tracker job, allowing Quartz to initiate an index update from
|
||||
* a {@link Tracker} regardless of specific implementation.
|
||||
*
|
||||
* Concurrent execution is disallowed, as no parallel work can be done when
|
||||
* indexing contents from the repository.
|
||||
*
|
||||
* @author Matt Ward
|
||||
*/
|
||||
@DisallowConcurrentExecution
|
||||
public class TrackerJob implements Job
|
||||
{
|
||||
public static final String JOBDATA_TRACKER_KEY = "TRACKER";
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ public class DefaultTrackerPoolFactory implements TrackerPoolFactory
|
||||
{
|
||||
case "AclTracker":
|
||||
corePoolSize = parseConfig("alfresco.acl.tracker.corePoolSize", p, corePoolSize);
|
||||
maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize);
|
||||
maximumPoolSize = parseConfig("alfresco.acl.tracker.maximumPoolSize", p, maximumPoolSize);
|
||||
keepAliveTime = parseConfig("alfresco.acl.tracker.keepAliveTime", p, keepAliveTime);
|
||||
threadPriority = parseConfig("alfresco.acl.tracker.threadPriority", p, threadPriority);
|
||||
threadDaemon = parseConfigBoolean("alfresco.acl.tracker.threadDaemon", p);
|
||||
|
||||
-4
@@ -42,11 +42,7 @@ import org.slf4j.LoggerFactory;
|
||||
public class DocValueDocTransformer extends DocTransformer
|
||||
{
|
||||
protected final static Logger log = LoggerFactory.getLogger(DocValueDocTransformer.class);
|
||||
|
||||
ResultContext context;
|
||||
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.solr.response.transform.DocTransformer#getName()
|
||||
*/
|
||||
|
||||
+4
-4
@@ -43,7 +43,7 @@ public class CoreDescriptorDecorator
|
||||
|
||||
private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms";
|
||||
|
||||
public static ImmutableList<String> substitutablePropertiesSecure =
|
||||
public static final ImmutableList<String> SUBSTITUTABLE_PROPERTIES_SECURE =
|
||||
ImmutableList.of(
|
||||
"alfresco.host",
|
||||
"alfresco.port",
|
||||
@@ -59,7 +59,7 @@ public class CoreDescriptorDecorator
|
||||
"alfresco.encryption.ssl.keystore.provider",
|
||||
"alfresco.encryption.ssl.truststore.type");
|
||||
|
||||
public static ImmutableList<String> substitutablePropertiesNone =
|
||||
public static final ImmutableList<String> SUBSTITUTABLE_PROPERTIES_NONE =
|
||||
ImmutableList.of(
|
||||
"alfresco.host",
|
||||
"alfresco.port",
|
||||
@@ -74,11 +74,11 @@ public class CoreDescriptorDecorator
|
||||
String comms = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, "none");
|
||||
if (comms.equals("https"))
|
||||
{
|
||||
coreProperties = substitutablePropertiesSecure;
|
||||
coreProperties = SUBSTITUTABLE_PROPERTIES_SECURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
coreProperties = substitutablePropertiesNone;
|
||||
coreProperties = SUBSTITUTABLE_PROPERTIES_NONE;
|
||||
}
|
||||
|
||||
try
|
||||
|
||||
+14
-1
@@ -30,6 +30,9 @@ alfresco.hole.check.after=300000
|
||||
alfresco.batch.count=5000
|
||||
alfresco.recordUnindexedNodes=false
|
||||
|
||||
# max time (in msecs) a given tracker instance will try to acquire a lock on a given DBID
|
||||
alfresco.tracker.maxNodeLockMs=120000
|
||||
|
||||
# encryption
|
||||
|
||||
# none, https
|
||||
@@ -119,12 +122,22 @@ solr.maxBooleanClauses=10000
|
||||
|
||||
# Batch fetch
|
||||
|
||||
alfresco.transactionDocsBatchSize=500
|
||||
#Max number of transactions fetched by metadata tracker
|
||||
#alfresco.maxNumberOfTransactions=
|
||||
|
||||
alfresco.transactionDocsBatchSize=2000
|
||||
alfresco.nodeBatchSize=100
|
||||
alfresco.changeSetAclsBatchSize=500
|
||||
alfresco.aclBatchSize=100
|
||||
alfresco.contentReadBatchSize=100
|
||||
alfresco.contentUpdateBatchSize=1000
|
||||
alfresco.cascadeNodeBatchSize=10
|
||||
|
||||
# Trackers thread pools
|
||||
#alfresco.metadataTrackerMaxParallelism=
|
||||
#alfresco.aclTrackerMaxParallelism=
|
||||
#alfresco.contentTrackerMaxParallelism=
|
||||
#alfresco.cascadeTrackerMaxParallelism
|
||||
|
||||
# Warming
|
||||
|
||||
|
||||
+2
-1
@@ -731,7 +731,8 @@ public abstract class AbstractAlfrescoDistributedIT extends SolrITInitializer
|
||||
|
||||
public static void indexTransaction(Transaction transaction, List<Node> nodes, List<NodeMetaData> nodeMetaDatas)
|
||||
{
|
||||
//First map the nodes to a transaction.
|
||||
// First map the nodes to a transaction.
|
||||
nodes.stream().forEach(node -> node.setTxnId(transaction.getId()));
|
||||
SOLRAPIQueueClient.NODE_MAP.put(transaction.getId(), nodes);
|
||||
|
||||
//Next map a node to the NodeMetaData
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ public class AlfrescoSolrReloadIT extends AbstractAlfrescoSolrIT
|
||||
singletonList(acl),
|
||||
singletonList(aclReaders));
|
||||
|
||||
int numNodes = 1;
|
||||
int numNodes = 1000;
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
List<NodeMetaData> nodeMetaDatas = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr;
|
||||
|
||||
public class CloudTest
|
||||
{
|
||||
/*
|
||||
public final static String QUERY = "a query";
|
||||
private Cloud cloud = new Cloud();
|
||||
@Mock SolrQueryRequest request;
|
||||
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
@Test
|
||||
public void testGetQueryWithZeroValues()
|
||||
{
|
||||
String fieldName = FIELD_DBID;
|
||||
String operator = SolrInformationServer.AND;
|
||||
Collection[] valueLists = new Collection[1];
|
||||
valueLists[0] = new ArrayList();
|
||||
String query = cloud.getQuery(fieldName, operator, valueLists);
|
||||
assertEquals("", query);
|
||||
|
||||
valueLists = new Collection[0];
|
||||
query = cloud.getQuery(fieldName, operator, valueLists);
|
||||
assertEquals("", query);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test
|
||||
public void testGetQueryWithOneValue()
|
||||
{
|
||||
String fieldName = FIELD_DBID;
|
||||
String operator = SolrInformationServer.AND;
|
||||
Collection[] valueLists = new Collection[1];
|
||||
valueLists[0] = new ArrayList();
|
||||
Object value = "value";
|
||||
valueLists[0].add(value);
|
||||
String query = cloud.getQuery(fieldName, operator, valueLists);
|
||||
assertEquals(FIELD_DBID + ":" + value, query);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test
|
||||
public void testGetQueryWithManyValues()
|
||||
{
|
||||
String fieldName = FIELD_DBID;
|
||||
String operator = SolrInformationServer.AND;
|
||||
Collection[] valueLists = new Collection[2];
|
||||
valueLists[0] = new ArrayList();
|
||||
Object value1 = "value1";
|
||||
valueLists[0].add(value1);
|
||||
Object value2 = "value2";
|
||||
valueLists[0].add(value2);
|
||||
Object value3 = "value3";
|
||||
valueLists[0].add(value3);
|
||||
valueLists[1] = new ArrayList();
|
||||
Object value4 = "value4";
|
||||
valueLists[1].add(value4);
|
||||
String query = cloud.getQuery(fieldName, operator, valueLists);
|
||||
assertEquals(FIELD_DBID + ":" + value1 + operator + FIELD_DBID + ":" + value2 + operator
|
||||
+ FIELD_DBID + ":" + value3 + operator + FIELD_DBID + ":" + value4, query);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExists()
|
||||
{
|
||||
boolean exists = cloud.exists(super.selectRequestHandler, request, QUERY);
|
||||
assertFalse(exists);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDocList()
|
||||
{
|
||||
// The response is created in the getDocList method, and the aftsRequestHandler is simply mocked.
|
||||
// Therefore nothing is expected to be on the response for tests, so this verifies behavior.
|
||||
DocList docList = cloud.getDocList(super.aftsRequestHandler, request, QUERY);
|
||||
assertNull(docList);
|
||||
|
||||
ArgumentCaptor<SolrQueryResponse> response = ArgumentCaptor.forClass(SolrQueryResponse.class);
|
||||
verify(super.aftsRequestHandler).handleRequest(eq(request), response.capture());
|
||||
assertNotNull(response.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelect()
|
||||
{
|
||||
SolrParams params = new ModifiableSolrParams(request.getParams());
|
||||
ResultContext rc = cloud.getResultContext(super.aftsRequestHandler, request, params);
|
||||
assertNull(rc);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.alfresco.solr;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.createCoreUsingTemplate;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
|
||||
import org.alfresco.solr.basics.RandomSupplier;
|
||||
import org.alfresco.solr.client.SOLRAPIQueueClient;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
+41
-22
@@ -1,5 +1,6 @@
|
||||
package org.alfresco.solr.basics;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.solr.client.solrj.SolrResponse;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
@@ -12,8 +13,10 @@ import org.slf4j.LoggerFactory;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SolrResponsesComparator
|
||||
{
|
||||
@@ -39,7 +42,7 @@ public class SolrResponsesComparator
|
||||
* Puts default values for handle
|
||||
*/
|
||||
public void putHandleDefaults() {
|
||||
handle.put("explain", SKIPVAL);
|
||||
handle.put("[explain]", SKIPVAL);
|
||||
handle.put("timestamp", SKIPVAL);
|
||||
handle.put("score", SKIPVAL);
|
||||
handle.put("wt", SKIP);
|
||||
@@ -51,6 +54,7 @@ public class SolrResponsesComparator
|
||||
handle.put("_version_", SKIP);
|
||||
handle.put("_original_parameters_", SKIP);
|
||||
handle.put("spellcheck-extras", SKIP); // No longer used can be removed in Solr 6.
|
||||
handle.put("FIELDS", UNORDERED);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,27 +89,6 @@ public class SolrResponsesComparator
|
||||
|
||||
public void compareResponses(QueryResponse a, QueryResponse b)
|
||||
{
|
||||
if (System.getProperty("remove.version.field") != null)
|
||||
{
|
||||
// we don't care if one has a version and the other doesnt -
|
||||
// control vs distrib
|
||||
// TODO: this should prob be done by adding an ignore on _version_
|
||||
// rather than mutating the responses?
|
||||
if (a.getResults() != null)
|
||||
{
|
||||
for (SolrDocument doc : a.getResults())
|
||||
{
|
||||
doc.removeFields("_version_");
|
||||
}
|
||||
}
|
||||
if (b.getResults() != null)
|
||||
{
|
||||
for (SolrDocument doc : b.getResults())
|
||||
{
|
||||
doc.removeFields("_version_");
|
||||
}
|
||||
}
|
||||
}
|
||||
compareSolrResponses(a, b);
|
||||
}
|
||||
|
||||
@@ -233,6 +216,29 @@ public class SolrResponsesComparator
|
||||
return compare1(b, a, flags, handle);
|
||||
}
|
||||
|
||||
public static String compare(Set a, Set b, int flags, Map<String, Integer> handle)
|
||||
{
|
||||
String cmp;
|
||||
cmp = compare1(a, b, flags, handle);
|
||||
if (cmp != null)
|
||||
return cmp;
|
||||
return compare1(b, a, flags, handle);
|
||||
}
|
||||
|
||||
private static String compare1(Set a, Set b, int flags, Map<String, Integer> handle) {
|
||||
for (Object valA : a)
|
||||
{
|
||||
int flagsa = flags(handle, valA);
|
||||
if ((flagsa & SKIP) != 0)
|
||||
continue;
|
||||
if (!b.contains(valA))
|
||||
{
|
||||
return "[" + valA + "]==null";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String compare(SolrDocument a, SolrDocument b, int flags, Map<String, Integer> handle)
|
||||
{
|
||||
return compare(a.getFieldValuesMap(), b.getFieldValuesMap(), flags, handle);
|
||||
@@ -315,10 +321,22 @@ public class SolrResponsesComparator
|
||||
|
||||
public static String compare(Object[] a, Object[] b, int flags, Map<String, Integer> handle)
|
||||
{
|
||||
|
||||
boolean ordered = (flags & UNORDERED) == 0;
|
||||
|
||||
if (a.length != b.length)
|
||||
{
|
||||
return ".length:" + a.length + "!=" + b.length;
|
||||
}
|
||||
|
||||
if (!ordered)
|
||||
{
|
||||
Set<Object> setA = Sets.newHashSet(a);
|
||||
Set<Object> setB = Sets.newHashSet(b);
|
||||
return compare(setA, setB, flags, handle);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < a.length; i++)
|
||||
{
|
||||
String cmp = compare(a[i], b[i], flags, handle);
|
||||
@@ -328,6 +346,7 @@ public class SolrResponsesComparator
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static String compare(Object a, Object b, int flags, Map<String, Integer> handle)
|
||||
{
|
||||
if (a == b)
|
||||
|
||||
+8
-7
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.ancestors;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.createGUID;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAcl;
|
||||
@@ -348,13 +350,11 @@ public class AlfrescoSolrTrackerExceptionIT extends AbstractAlfrescoSolrIT
|
||||
|
||||
logger.info("#################### Passed Fifteenth Test ##############################");
|
||||
|
||||
|
||||
List<String> readers = aclReaders.getReaders();
|
||||
readers.set(0, "andy"); // Change the aclReader
|
||||
// Change the aclReaders
|
||||
aclReaders.setReaders(asList("andy"));
|
||||
indexAclId(acl.getId());
|
||||
|
||||
List<String> readers2 = aclReaders2.getReaders();
|
||||
readers2.set(0, "ice"); // Change the aclReader
|
||||
aclReaders2.setReaders(asList("ice"));
|
||||
reindexAclId(acl2.getId());
|
||||
|
||||
|
||||
@@ -377,8 +377,9 @@ public class AlfrescoSolrTrackerExceptionIT extends AbstractAlfrescoSolrIT
|
||||
|
||||
logger.info("#################### Passed Seventeenth Test ##############################");
|
||||
|
||||
readers.set(0, "alan"); // Change the aclReader
|
||||
readers2.set(0, "paul"); // Change the aclReader
|
||||
// Change the aclReaders
|
||||
aclReaders.setReaders(asList("alan"));
|
||||
aclReaders2.setReaders(asList("paul"));
|
||||
|
||||
reindexAclChangeSetId(aclChangeSet.getId()); //This should replace "andy" and "ice" with "alan" and "paul"
|
||||
|
||||
|
||||
+8
-7
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.ancestors;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.createGUID;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAcl;
|
||||
@@ -353,13 +355,11 @@ public class AlfrescoSolrTrackerIT extends AbstractAlfrescoSolrIT
|
||||
|
||||
logger.info("#################### Passed Fifteenth Test ##############################");
|
||||
|
||||
|
||||
List<String> readers = aclReaders.getReaders();
|
||||
readers.set(0, "andy"); // Change the aclReader
|
||||
// Change the aclReaders
|
||||
aclReaders.setReaders(asList("andy"));
|
||||
indexAclId(acl.getId());
|
||||
|
||||
List<String> readers2 = aclReaders2.getReaders();
|
||||
readers2.set(0, "ice"); // Change the aclReader
|
||||
aclReaders2.setReaders(asList("ice"));
|
||||
reindexAclId(acl2.getId());
|
||||
|
||||
|
||||
@@ -382,8 +382,9 @@ public class AlfrescoSolrTrackerIT extends AbstractAlfrescoSolrIT
|
||||
|
||||
logger.info("#################### Passed Seventeenth Test ##############################");
|
||||
|
||||
readers.set(0, "alan"); // Change the aclReader
|
||||
readers2.set(0, "paul"); // Change the aclReader
|
||||
// Change the aclReaders
|
||||
aclReaders.setReaders(asList("alan"));
|
||||
aclReaders2.setReaders(asList("paul"));
|
||||
|
||||
reindexAclChangeSetId(aclChangeSet.getId()); //This should replace "andy" and "ice" with "alan" and "paul"
|
||||
|
||||
|
||||
+4
-6
@@ -53,14 +53,12 @@ public class ContentTrackerIT
|
||||
private TrackerStats trackerStats;
|
||||
|
||||
private int UPDATE_BATCH = 2;
|
||||
private int READ_BATCH = 400;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
doReturn("workspace://SpacesStore").when(props).getProperty(eq("alfresco.stores"), anyString());
|
||||
doReturn("" + UPDATE_BATCH).when(props).getProperty(eq("alfresco.contentUpdateBatchSize"), anyString());
|
||||
doReturn("" + READ_BATCH).when(props).getProperty(eq("alfresco.contentReadBatchSize"), anyString());
|
||||
when(srv.getTrackerStats()).thenReturn(trackerStats);
|
||||
this.contentTracker = new ContentTracker(props, repositoryClient, coreName, srv);
|
||||
|
||||
@@ -103,14 +101,14 @@ public class ContentTrackerIT
|
||||
doc.tenant = "2";
|
||||
docs2.add(doc);
|
||||
}
|
||||
when(this.srv.getDocsWithUncleanContent(anyInt(), anyInt()))
|
||||
when(this.srv.getDocsWithUncleanContent())
|
||||
.thenReturn(docs1)
|
||||
.thenReturn(docs2)
|
||||
.thenReturn(emptyList);
|
||||
this.contentTracker.doTrack("anIterationId");
|
||||
|
||||
InOrder order = inOrder(srv);
|
||||
order.verify(srv).getDocsWithUncleanContent(0, READ_BATCH);
|
||||
order.verify(srv).getDocsWithUncleanContent();
|
||||
|
||||
/*
|
||||
* I had to make each bunch of calls have different parameters to prevent Mockito from incorrectly failing
|
||||
@@ -131,7 +129,7 @@ public class ContentTrackerIT
|
||||
order.verify(srv).updateContent(thirdDoc);
|
||||
order.verify(srv).commit();
|
||||
|
||||
order.verify(srv).getDocsWithUncleanContent(0 + READ_BATCH, READ_BATCH);
|
||||
order.verify(srv).getDocsWithUncleanContent();
|
||||
|
||||
// From docs2
|
||||
docRef = new TenantDbId();
|
||||
@@ -140,7 +138,7 @@ public class ContentTrackerIT
|
||||
order.verify(srv, times(UPDATE_BATCH)).updateContent(docRef);
|
||||
order.verify(srv).commit();
|
||||
|
||||
order.verify(srv).getDocsWithUncleanContent(0 + READ_BATCH + READ_BATCH, READ_BATCH);
|
||||
order.verify(srv).getDocsWithUncleanContent();
|
||||
}
|
||||
@Test
|
||||
public void typeCheck()
|
||||
|
||||
+13
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.solr.AbstractAlfrescoDistributedIT;
|
||||
import org.alfresco.solr.SolrInformationServer;
|
||||
import org.alfresco.solr.client.Acl;
|
||||
@@ -27,6 +28,9 @@ import org.alfresco.solr.client.Node;
|
||||
import org.alfresco.solr.client.NodeMetaData;
|
||||
import org.alfresco.solr.client.Transaction;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.LegacyNumericRangeQuery;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
@@ -42,6 +46,7 @@ import java.util.Properties;
|
||||
|
||||
import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_DOC_TYPE;
|
||||
import static org.alfresco.repo.search.adaptor.lucene.QueryConstants.FIELD_SOLR4_ID;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.MAX_WAIT_TIME;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAcl;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAclChangeSet;
|
||||
import static org.alfresco.solr.AlfrescoSolrUtils.getAclReaders;
|
||||
@@ -100,6 +105,14 @@ public class DistributedExpandDbidRangeAlfrescoSolrTrackerIT extends AbstractAlf
|
||||
bulkAcls,
|
||||
bulkAclReaders);
|
||||
|
||||
//Check for the ACL state stamp.
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.add(new BooleanClause(new TermQuery(new Term(QueryConstants.FIELD_SOLR4_ID, "TRACKER!STATE!ACLTX")), BooleanClause.Occur.MUST));
|
||||
builder.add(new BooleanClause(LegacyNumericRangeQuery.newLongRange(QueryConstants.FIELD_S_ACLTXID,
|
||||
bulkAclChangeSet.getId(), bulkAclChangeSet.getId() + 1, true, false), BooleanClause.Occur.MUST));
|
||||
BooleanQuery waitForQuery = builder.build();
|
||||
waitForDocCount(waitForQuery, 1, MAX_WAIT_TIME);
|
||||
|
||||
SolrQueryResponse response0 = rangeCheck(0);
|
||||
NamedList values0 = response0.getValues();
|
||||
//{start=0,end=100,nodeCount=0,maxDbid=0,density=NaN,expand=0,expanded=false}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.solr.tracker;
|
||||
|
||||
import static org.alfresco.solr.tracker.DocRouterFactory.SHARD_RANGE_KEY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.index.shard.ShardMethodEnum;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Unit tests for the {@link DocRouterFactory}. */
|
||||
public class DocRouterFactoryTest
|
||||
{
|
||||
/** Check that a DB_ID_RANGE router can be created. */
|
||||
@Test
|
||||
public void testDBIDRANGEWithShardRangeKey()
|
||||
{
|
||||
Properties mockProperties = mock(Properties.class);
|
||||
when(mockProperties.containsKey(SHARD_RANGE_KEY)).thenReturn(true);
|
||||
when(mockProperties.getProperty(SHARD_RANGE_KEY)).thenReturn("100000000-150000000");
|
||||
|
||||
// Call the method under test.
|
||||
DocRouter docRouter = DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE);
|
||||
|
||||
assertTrue("Expected to get a DBIDRangeRouter.", docRouter instanceof DBIDRangeRouter);
|
||||
DBIDRangeRouter dbidRangeRouter = (DBIDRangeRouter) docRouter;
|
||||
assertEquals("Unexpected start of range.", dbidRangeRouter.getStartRange(), 100000000L);
|
||||
assertEquals("Unexpected end of range.", dbidRangeRouter.getEndRange(), 150000000L);
|
||||
}
|
||||
|
||||
/** Check that an exception is raised if the range information is missing. */
|
||||
@Test(expected = AlfrescoRuntimeException.class)
|
||||
public void testDBIDRANGEWithoutShardRangeKey()
|
||||
{
|
||||
Properties mockProperties = mock(Properties.class);
|
||||
DocRouterFactory.getRouter(mockProperties, ShardMethodEnum.DB_ID_RANGE);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -82,7 +82,7 @@ public class DefaultTrackerPoolFactoryTest
|
||||
public void testAclDefaultProperties()
|
||||
{
|
||||
poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker");
|
||||
|
||||
|
||||
tpe = poolFactory.create();
|
||||
assertEquals(4, tpe.getCorePoolSize());
|
||||
assertEquals(4, tpe.getMaximumPoolSize());
|
||||
@@ -96,7 +96,7 @@ public class DefaultTrackerPoolFactoryTest
|
||||
properties.put("alfresco.acl.tracker.keepAliveTime", "200");
|
||||
poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "AclTracker");
|
||||
tpe = poolFactory.create();
|
||||
|
||||
|
||||
assertEquals(30, tpe.getCorePoolSize());
|
||||
assertEquals(40, tpe.getMaximumPoolSize());
|
||||
assertEquals(200, tpe.getKeepAliveTime(TimeUnit.SECONDS));
|
||||
@@ -120,7 +120,7 @@ public class DefaultTrackerPoolFactoryTest
|
||||
properties.put("alfresco.content.tracker.keepAliveTime", "201");
|
||||
poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "ContentTracker");
|
||||
tpe = poolFactory.create();
|
||||
|
||||
|
||||
assertEquals(100, tpe.getCorePoolSize());
|
||||
assertEquals(140, tpe.getMaximumPoolSize());
|
||||
assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS));
|
||||
@@ -129,9 +129,9 @@ public class DefaultTrackerPoolFactoryTest
|
||||
public void testMetaDataDefaultProperties()
|
||||
{
|
||||
poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker");
|
||||
|
||||
|
||||
tpe = poolFactory.create();
|
||||
|
||||
|
||||
assertEquals(4, tpe.getCorePoolSize());
|
||||
assertEquals(4, tpe.getMaximumPoolSize());
|
||||
assertEquals(120, tpe.getKeepAliveTime(TimeUnit.SECONDS));
|
||||
@@ -143,9 +143,9 @@ public class DefaultTrackerPoolFactoryTest
|
||||
properties.put("alfresco.metadata.tracker.maximumPoolSize", "140");
|
||||
properties.put("alfresco.metadata.tracker.keepAliveTime", "201");
|
||||
poolFactory = new DefaultTrackerPoolFactory(properties, "TheCore", "MetadataTracker");
|
||||
|
||||
|
||||
tpe = poolFactory.create();
|
||||
|
||||
|
||||
assertEquals(100, tpe.getCorePoolSize());
|
||||
assertEquals(140, tpe.getMaximumPoolSize());
|
||||
assertEquals(201, tpe.getKeepAliveTime(TimeUnit.SECONDS));
|
||||
|
||||
+10
-9
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.transformer;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
@@ -81,7 +82,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
//Test 1: Running a simple query without invoking AlfrescoFieldMapperTransformer, expected to see id,DBID and _version_
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts", "sort", "id asc"));
|
||||
assertNotNull(resp);
|
||||
SolrDocumentList results = resp.getResults();
|
||||
assertEquals("Expecting 5 rows",5, results.size());
|
||||
@@ -107,7 +108,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
putHandleDefaults();
|
||||
|
||||
//Test 2: Running simple query with AlfrescoFieldMapperTransformer, expected to see all fields returned
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","*,[fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","*,[fmap]", "sort", "id asc"));
|
||||
SolrDocument docWithAllFields = resp.getResults().get(0);
|
||||
assertTrue(docWithAllFields.size() > 3);
|
||||
|
||||
@@ -129,7 +130,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
putHandleDefaults();
|
||||
|
||||
//Test 3: Running simple query with AlfrescoFieldMapperTransformer, expected to see selected fields returned
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id,DBID,[fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id,DBID,[fmap]", "sort", "id asc"));
|
||||
|
||||
assertNotNull(resp);
|
||||
SolrDocument docWithRequestedFields = resp.getResults().get(0);
|
||||
@@ -144,7 +145,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
putHandleDefaults();
|
||||
|
||||
//Test 4: Running simple query with fmapDocTransformer on non default fields, expected to see selected fields returned
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id, cm_title,[fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id, cm_title,[fmap]", "sort", "id asc"));
|
||||
|
||||
assertNotNull(resp);
|
||||
SolrDocument docWithRequestedFields3 = resp.getResults().get(0);
|
||||
@@ -159,7 +160,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id, cm_title,[fmap], [fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","id, cm_title,[fmap], [fmap]", "sort", "id asc"));
|
||||
|
||||
assertNotNull(resp);
|
||||
SolrDocument docWithRequestedFields3 = resp.getResults().get(0);
|
||||
@@ -174,7 +175,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_name, score, [fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_name, score, [fmap]", "sort", "id asc"));
|
||||
assertNotNull(resp);
|
||||
SolrDocumentList results = resp.getResults();
|
||||
SolrDocument docWithAllFields = results.get(0);
|
||||
@@ -189,7 +190,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_title, cm_created, DBID, score, [fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_title, cm_created, DBID, score, [fmap]", "sort", "id asc"));
|
||||
assertNotNull(resp);
|
||||
SolrDocumentList results = resp.getResults();
|
||||
SolrDocument docWithAllFields = results.get(0);
|
||||
@@ -205,7 +206,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm?title, *name, [fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm?title, *name, [fmap]", "sort", "id asc"));
|
||||
assertNotNull(resp);
|
||||
SolrDocumentList results = resp.getResults();
|
||||
SolrDocument docWithAllFields = results.get(0);
|
||||
@@ -220,7 +221,7 @@ public class AlfrescoFieldMapperTransformerIT extends AbstractAlfrescoDistribute
|
||||
{
|
||||
putHandleDefaults();
|
||||
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_title, [explain], [fmap]"));
|
||||
QueryResponse resp = query(getDefaultTestClient(), true, ALFRESCO_JSON, params("q", "*", "qt", "/afts", "shards.qt", "/afts","fl","cm_title, [explain], [fmap]", "sort", "id asc"));
|
||||
assertNotNull(resp);
|
||||
SolrDocumentList results = resp.getResults();
|
||||
SolrDocument docWithAllFields = results.get(0);
|
||||
|
||||
-567
@@ -1,567 +0,0 @@
|
||||
package org.apache.lucene.analysis.minhash;
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.BaseTokenStreamTestCase;
|
||||
import org.apache.lucene.analysis.MockTokenizer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
|
||||
import org.apache.lucene.analysis.minhash.MinHashFilter.FixedSizeTreeSet;
|
||||
import org.apache.lucene.analysis.minhash.MinHashFilter.LongPair;
|
||||
import org.apache.lucene.analysis.shingle.ShingleFilterFactory;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
import org.apache.lucene.analysis.util.CharFilterFactory;
|
||||
import org.apache.lucene.analysis.util.TokenFilterFactory;
|
||||
import org.apache.lucene.analysis.util.TokenizerFactory;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
|
||||
import org.apache.lucene.util.automaton.RegExp;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MinHashFilterIT extends BaseTokenStreamTestCase
|
||||
{
|
||||
@Test
|
||||
public void testIntHash() {
|
||||
LongPair hash = new LongPair();
|
||||
MinHashFilter.murmurhash3_x64_128(MinHashFilter.getBytes(0), 0, 4, 0, hash);
|
||||
assertEquals(-3485513579396041028L, hash.val1);
|
||||
assertEquals(6383328099726337777L, hash.val2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringHash() throws UnsupportedEncodingException {
|
||||
LongPair hash = new LongPair();
|
||||
byte[] bytes = "woof woof woof woof woof".getBytes("UTF-16LE");
|
||||
MinHashFilter.murmurhash3_x64_128(bytes, 0, bytes.length, 0, hash);
|
||||
assertEquals(7638079586852243959L, hash.val1);
|
||||
assertEquals(4378804943379391304L, hash.val2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleOrder() throws UnsupportedEncodingException {
|
||||
LongPair hash1 = new LongPair();
|
||||
hash1.val1 = 1;
|
||||
hash1.val2 = 2;
|
||||
LongPair hash2 = new LongPair();
|
||||
hash2.val1 = 2;
|
||||
hash2.val2 = 1;
|
||||
assert (hash1.compareTo(hash2) > 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHashOrder() {
|
||||
assertTrue(!MinHashFilter.isLessThanUnsigned(0l, 0l));
|
||||
assertTrue(MinHashFilter.isLessThanUnsigned(0l, -1l));
|
||||
assertTrue(MinHashFilter.isLessThanUnsigned(1l, -1l));
|
||||
assertTrue(MinHashFilter.isLessThanUnsigned(-2l, -1l));
|
||||
assertTrue(MinHashFilter.isLessThanUnsigned(1l, 2l));
|
||||
assertTrue(MinHashFilter.isLessThanUnsigned(Long.MAX_VALUE, Long.MIN_VALUE));
|
||||
|
||||
FixedSizeTreeSet<LongPair> minSet = new FixedSizeTreeSet<LongPair>(500);
|
||||
HashSet<LongPair> unadded = new HashSet<LongPair>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
LongPair hash = new LongPair();
|
||||
MinHashFilter.murmurhash3_x64_128(MinHashFilter.getBytes(i), 0, 4, 0, hash);
|
||||
LongPair peek = null;
|
||||
if (minSet.size() > 0) {
|
||||
peek = minSet.last();
|
||||
}
|
||||
|
||||
if (!minSet.add(hash)) {
|
||||
unadded.add(hash);
|
||||
} else {
|
||||
if (peek != null) {
|
||||
if ((minSet.size() == 500) && !peek.equals(minSet.last())) {
|
||||
unadded.add(peek);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(100, minSet.size());
|
||||
assertEquals(0, unadded.size());
|
||||
|
||||
HashSet<LongPair> collisionDetection = new HashSet<LongPair>();
|
||||
unadded = new HashSet<LongPair>();
|
||||
minSet = new FixedSizeTreeSet<LongPair>(500);
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
LongPair hash = new LongPair();
|
||||
MinHashFilter.murmurhash3_x64_128(MinHashFilter.getBytes(i), 0, 4, 0, hash);
|
||||
collisionDetection.add(hash);
|
||||
LongPair peek = null;
|
||||
if (minSet.size() > 0) {
|
||||
peek = minSet.last();
|
||||
}
|
||||
|
||||
if (!minSet.add(hash)) {
|
||||
unadded.add(hash);
|
||||
} else {
|
||||
if (peek != null) {
|
||||
if ((minSet.size() == 500) && !peek.equals(minSet.last())) {
|
||||
unadded.add(peek);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(1000000, collisionDetection.size());
|
||||
assertEquals(500, minSet.size());
|
||||
assertEquals(999500, unadded.size());
|
||||
|
||||
LongPair last = null;
|
||||
LongPair current = null;
|
||||
while ((current = minSet.pollLast()) != null) {
|
||||
if (last != null) {
|
||||
assertTrue(isLessThan(current, last));
|
||||
} else {
|
||||
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testHashNotRepeated() {
|
||||
FixedSizeTreeSet<LongPair> minSet = new FixedSizeTreeSet<LongPair>(500);
|
||||
HashSet<LongPair> unadded = new HashSet<LongPair>();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
LongPair hash = new LongPair();
|
||||
MinHashFilter.murmurhash3_x64_128(MinHashFilter.getBytes(i), 0, 4, 0, hash);
|
||||
LongPair peek = null;
|
||||
if (minSet.size() > 0) {
|
||||
peek = minSet.last();
|
||||
}
|
||||
if (!minSet.add(hash)) {
|
||||
unadded.add(hash);
|
||||
} else {
|
||||
if (peek != null) {
|
||||
if ((minSet.size() == 500) && !peek.equals(minSet.last())) {
|
||||
unadded.add(peek);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(500, minSet.size());
|
||||
|
||||
LongPair last = null;
|
||||
LongPair current = null;
|
||||
while ((current = minSet.pollLast()) != null) {
|
||||
if (last != null) {
|
||||
assertTrue(isLessThan(current, last));
|
||||
} else {
|
||||
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMockShingleTokenizer() throws IOException {
|
||||
Tokenizer mockShingleTokenizer = createMockShingleTokenizer(5,
|
||||
"woof woof woof woof woof" + " " + "woof woof woof woof puff");
|
||||
assertTokenStreamContents(mockShingleTokenizer,
|
||||
new String[] {"woof woof woof woof woof", "woof woof woof woof puff"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStreamSingleInput() throws IOException {
|
||||
String[] hashes = new String[] {"℁팽徭聙↝ꇁ홱杯"};
|
||||
TokenStream ts = createTokenStream(5, "woof woof woof woof woof", 1, 1, 100, false);
|
||||
assertTokenStreamContents(ts, hashes, new int[] {0},
|
||||
new int[] {24}, new String[] {MinHashFilter.MIN_HASH_TYPE}, new int[] {1}, new int[] {1}, 24, 0, null,
|
||||
true);
|
||||
|
||||
ts = createTokenStream(5, "woof woof woof woof woof", 2, 1, 1, false);
|
||||
assertTokenStreamContents(ts, new String[] {new String(new char[] {0, 0, 8449, 54077, 64133, 32857, 8605, 41409}),
|
||||
new String(new char[] {0, 1, 16887, 58164, 39536, 14926, 6529, 17276})}, new int[] {0, 0},
|
||||
new int[] {24, 24}, new String[] {MinHashFilter.MIN_HASH_TYPE, MinHashFilter.MIN_HASH_TYPE}, new int[] {1, 0}, new int[] {1, 1}, 24, 0, null,
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStream1() throws IOException {
|
||||
String[] hashes = new String[] {"℁팽徭聙↝ꇁ홱杯",
|
||||
new String(new char[] {36347, 63457, 43013, 56843, 52284, 34231, 57934, 42302})};
|
||||
|
||||
TokenStream ts = createTokenStream(5, "woof woof woof woof woof" + " " + "woof woof woof woof puff", 1, 1, 100,false);
|
||||
assertTokenStreamContents(ts, hashes, new int[] {0, 0},
|
||||
new int[] {49, 49}, new String[] {MinHashFilter.MIN_HASH_TYPE, MinHashFilter.MIN_HASH_TYPE}, new int[] {1, 0},
|
||||
new int[] {1, 1}, 49, 0, null, true);
|
||||
}
|
||||
|
||||
private ArrayList<String> getTokens(TokenStream ts) throws IOException {
|
||||
ArrayList<String> tokens = new ArrayList<String>();
|
||||
ts.reset();
|
||||
while (ts.incrementToken()) {
|
||||
CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class);
|
||||
String token = new String(termAttribute.buffer(), 0, termAttribute.length());
|
||||
tokens.add(token);
|
||||
}
|
||||
ts.end();
|
||||
ts.close();
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private ArrayList<String> getTokens(Analyzer analyzer, String field, String value) throws IOException
|
||||
{
|
||||
ArrayList<String> tokens = new ArrayList<String>();
|
||||
|
||||
TokenStream ts = analyzer.tokenStream(field, value);
|
||||
ts.reset();
|
||||
while(ts.incrementToken())
|
||||
{
|
||||
CharTermAttribute termAttribute = ts.getAttribute(CharTermAttribute.class);
|
||||
String token = new String(termAttribute.buffer(), 0, termAttribute.length());
|
||||
tokens.add(token);
|
||||
}
|
||||
ts.end();
|
||||
ts.close();
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStream2() throws IOException {
|
||||
TokenStream ts = createTokenStream(5, "woof woof woof woof woof" + " " + "woof woof woof woof puff", 100, 1, 1, false);
|
||||
ArrayList<String> tokens = getTokens(ts);
|
||||
ts.close();
|
||||
|
||||
assertEquals(100, tokens.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStream3() throws IOException {
|
||||
TokenStream ts = createTokenStream(5, "woof woof woof woof woof" + " " + "woof woof woof woof puff", 10, 1, 10, false);
|
||||
ArrayList<String> tokens = getTokens(ts);
|
||||
ts.close();
|
||||
|
||||
assertEquals(20, tokens.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStream4() throws IOException {
|
||||
TokenStream ts = createTokenStream(5, "woof woof woof woof woof" + " " + "woof woof woof woof puff", 10, 10, 1, false);
|
||||
ArrayList<String> tokens = getTokens(ts);
|
||||
ts.close();
|
||||
|
||||
assertEquals(20, tokens.size());
|
||||
|
||||
ts = createTokenStream(5, "woof woof woof woof woof" + " " + "woof woof woof woof puff", 10, 10, 1, true);
|
||||
tokens = getTokens(ts);
|
||||
ts.close();
|
||||
|
||||
assertEquals(100, tokens.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLSHQuery() throws IOException
|
||||
{
|
||||
Analyzer analyzer = createMinHashAnalyzer(5, 1, 100);
|
||||
IndexWriterConfig config = new IndexWriterConfig(analyzer);
|
||||
|
||||
RAMDirectory directory = new RAMDirectory();
|
||||
IndexWriter writer = new IndexWriter(directory, config);
|
||||
Document doc = new Document();
|
||||
doc.add(new TextField("text", "woof woof woof woof woof", Store.NO));
|
||||
writer.addDocument(doc);
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new TextField("text", "woof woof woof woof woof puff", Store.NO));
|
||||
writer.addDocument(doc);
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new TextField("text", "woof woof woof woof puff", Store.NO));
|
||||
writer.addDocument(doc);
|
||||
|
||||
writer.commit();
|
||||
writer.close();
|
||||
|
||||
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory));
|
||||
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
builder.add(new ConstantScoreQuery(new TermQuery(new Term("text", "℁팽徭聙↝ꇁ홱杯"))), Occur.SHOULD);
|
||||
builder.add(new ConstantScoreQuery(new TermQuery(new Term("text", new String(new char[] {36347, 63457, 43013, 56843, 52284, 34231, 57934, 42302})))), Occur.SHOULD);
|
||||
builder.setDisableCoord(true);
|
||||
TopDocs topDocs = searcher.search(builder.build(), 10);
|
||||
|
||||
assertEquals(3, topDocs.totalHits);
|
||||
|
||||
float score = topDocs.scoreDocs[0].score;
|
||||
assertEquals(topDocs.scoreDocs[1].score, score/2, 0f);
|
||||
assertEquals(topDocs.scoreDocs[2].score, score/2, 0f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testLSHQuery2() throws IOException
|
||||
{
|
||||
String[] parts = new String[]{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
|
||||
int min = 5;
|
||||
|
||||
Analyzer analyzer = createMinHashAnalyzer(min, 1, 100);
|
||||
IndexWriterConfig config = new IndexWriterConfig(analyzer);
|
||||
|
||||
RAMDirectory directory = new RAMDirectory();
|
||||
IndexWriter writer = new IndexWriter(directory, config);
|
||||
|
||||
for(int i = 0; i < parts.length; i++)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(int j = 0; j < parts.length -i; j++)
|
||||
{
|
||||
if(builder.length() > 0)
|
||||
{
|
||||
builder.append(" ");
|
||||
}
|
||||
builder.append(parts[i+j]);
|
||||
if(j >= min -1)
|
||||
{
|
||||
Document doc = new Document();
|
||||
doc.add(new TextField("text", builder.toString(), Store.NO));
|
||||
writer.addDocument(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.commit();
|
||||
writer.close();
|
||||
|
||||
|
||||
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory));
|
||||
|
||||
TopDocs topDocs = searcher.search(buildQuery("text", "one two three four five", min, 1, 100), 100);
|
||||
assertEquals(6, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
topDocs = searcher.search(buildQuery("text", "two three four five six", min, 1, 100), 100);
|
||||
assertEquals(10, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
topDocs = searcher.search(buildQuery("text", "three four five six seven", min, 1, 100), 100);
|
||||
assertEquals(12, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
topDocs = searcher.search(buildQuery("text", "four five six seven eight", min, 1, 100), 100);
|
||||
assertEquals(12, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
topDocs = searcher.search(buildQuery("text", "five six seven eight nine", min, 1, 100), 100);
|
||||
assertEquals(10, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
topDocs = searcher.search(buildQuery("text", "six seven eight nine ten", min, 1, 100), 100);
|
||||
assertEquals(6, topDocs.totalHits);
|
||||
assertAllScores(topDocs, 1.0f);
|
||||
|
||||
topDocs = searcher.search(buildQuery("text", "one two three four five six", min, 1, 100), 100);
|
||||
assertEquals(11, topDocs.totalHits);
|
||||
|
||||
topDocs = searcher.search(buildQuery("text", "one two three four five six seven eight nine ten", min, 1, 100), 100);
|
||||
assertEquals(21, topDocs.totalHits);
|
||||
for(int i = 0; i < topDocs.totalHits; i++)
|
||||
{
|
||||
System.out.println(i+" = "+topDocs.scoreDocs[i]);
|
||||
}
|
||||
|
||||
float topScore = 6.0f;
|
||||
assertEquals(topDocs.scoreDocs[0].score, topScore, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[1].score, topScore * 5/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[2].score, topScore * 5/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[3].score, topScore * 4/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[4].score, topScore * 4/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[5].score, topScore * 4/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[6].score, topScore * 3/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[7].score, topScore * 3/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[8].score, topScore * 3/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[9].score, topScore * 3/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[10].score, topScore * 2/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[11].score, topScore * 2/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[12].score, topScore * 2/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[13].score, topScore * 2/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[14].score, topScore * 2/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[15].score, topScore * 1/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[16].score, topScore * 1/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[17].score, topScore * 1/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[18].score, topScore * 1/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[19].score, topScore * 1/6, 0.001f);
|
||||
assertEquals(topDocs.scoreDocs[20].score, topScore * 1/6, 0.001f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void assertAllScores(TopDocs topDocs, float score)
|
||||
{
|
||||
for(int i = 0; i < topDocs.totalHits; i++)
|
||||
{
|
||||
assertEquals(topDocs.scoreDocs[i].score, score, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private Query buildQuery(String field, String query, int min, int hashCount, int hashSetSize) throws IOException
|
||||
{
|
||||
TokenizerChain chain = createMinHashAnalyzer(min, hashCount, hashSetSize);
|
||||
ArrayList<String> tokens = getTokens(chain, field, query);
|
||||
chain.close();
|
||||
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
for(String token : tokens)
|
||||
{
|
||||
builder.add(new ConstantScoreQuery(new TermQuery(new Term("text", token))), Occur.SHOULD);
|
||||
}
|
||||
builder.setDisableCoord(true);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static TokenStream createTokenStream(int shingleSize, String shingles, int hashCount, int bucketCount, int hashSetSize, boolean withRotation) {
|
||||
Tokenizer tokenizer = createMockShingleTokenizer(shingleSize, shingles);
|
||||
HashMap<String,String> lshffargs = new HashMap<String,String>();
|
||||
lshffargs.put("hashCount", "" + hashCount);
|
||||
lshffargs.put("bucketCount", "" + bucketCount);
|
||||
lshffargs.put("hashSetSize", "" + hashSetSize);
|
||||
lshffargs.put("withRotation", "" + withRotation);
|
||||
MinHashFilterFactory lshff = new MinHashFilterFactory(lshffargs);
|
||||
return lshff.create(tokenizer);
|
||||
}
|
||||
|
||||
public static TokenizerChain createMinHashAnalyzer(int min, int hashCount, int hashSetSize)
|
||||
{
|
||||
WhitespaceTokenizerFactory icutf = new WhitespaceTokenizerFactory(Collections.<String, String>emptyMap());
|
||||
HashMap<String, String> sffargs = new HashMap<String, String>();
|
||||
sffargs.put("minShingleSize", ""+min);
|
||||
sffargs.put("maxShingleSize", ""+min);
|
||||
sffargs.put("outputUnigrams", "false");
|
||||
sffargs.put("outputUnigramsIfNoShingles", "false");
|
||||
sffargs.put("tokenSeparator", " ");
|
||||
ShingleFilterFactory sff = new ShingleFilterFactory(sffargs);
|
||||
HashMap<String, String> lshffargs = new HashMap<String, String>();
|
||||
lshffargs.put("hashCount", ""+hashCount);
|
||||
lshffargs.put("hashSetSize", ""+hashSetSize);
|
||||
MinHashFilterFactory lshff = new MinHashFilterFactory(lshffargs);
|
||||
|
||||
TokenizerChain chain = new TokenizerChain(new CharFilterFactory[]{}, icutf, new TokenFilterFactory[]{sff, lshff});
|
||||
return chain;
|
||||
}
|
||||
|
||||
public static Tokenizer createMockShingleTokenizer(int shingleSize, String shingles) {
|
||||
MockTokenizer tokenizer = new MockTokenizer(
|
||||
new CharacterRunAutomaton(new RegExp("[^ \t\r\n]+([ \t\r\n]+[^ \t\r\n]+){4}").toAutomaton()),
|
||||
true);
|
||||
tokenizer.setEnableChecks(true);
|
||||
if (shingles != null) {
|
||||
tokenizer.setReader(new StringReader(shingles));
|
||||
}
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
private boolean isLessThan(LongPair hash1, LongPair hash2) {
|
||||
if (MinHashFilter.isLessThanUnsigned(hash1.val2, hash2.val2)) {
|
||||
return true;
|
||||
} else if (hash1.val2 == hash2.val2) {
|
||||
return (MinHashFilter.isLessThanUnsigned(hash1.val1, hash2.val1));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An analyzer that uses a tokenizer and a list of token filters to
|
||||
* create a TokenStream - lifted from SOLR to make this analyzer test lucene only.
|
||||
*/
|
||||
public static class TokenizerChain extends Analyzer {
|
||||
|
||||
final private CharFilterFactory[] charFilters;
|
||||
final private TokenizerFactory tokenizer;
|
||||
final private TokenFilterFactory[] filters;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new TokenizerChain.
|
||||
*
|
||||
* @param charFilters Factories for the CharFilters to use, if any - if null, will be treated as if empty.
|
||||
* @param tokenizer Factory for the Tokenizer to use, must not be null.
|
||||
* @param filters Factories for the TokenFilters to use if any- if null, will be treated as if empty.
|
||||
*/
|
||||
public TokenizerChain(CharFilterFactory[] charFilters, TokenizerFactory tokenizer, TokenFilterFactory[] filters) {
|
||||
this.charFilters = charFilters;
|
||||
this.tokenizer = tokenizer;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader initReader(String fieldName, Reader reader) {
|
||||
if (charFilters != null && charFilters.length > 0) {
|
||||
Reader cs = reader;
|
||||
for (CharFilterFactory charFilter : charFilters) {
|
||||
cs = charFilter.create(cs);
|
||||
}
|
||||
reader = cs;
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TokenStreamComponents createComponents(String fieldName) {
|
||||
Tokenizer tk = tokenizer.create();
|
||||
TokenStream ts = tk;
|
||||
for (TokenFilterFactory filter : filters) {
|
||||
ts = filter.create(ts);
|
||||
}
|
||||
return new TokenStreamComponents(tk, ts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("TokenizerChain(");
|
||||
for (CharFilterFactory filter: charFilters) {
|
||||
sb.append(filter);
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(tokenizer);
|
||||
for (TokenFilterFactory filter: filters) {
|
||||
sb.append(", ");
|
||||
sb.append(filter);
|
||||
}
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,6 @@
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<!-- Useful tests categories -->
|
||||
<category name="org.alfresco.solr.AbstractAlfrescoSolrIT">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<!-- ##### ALL ALFRESCO MESSAGES ##### -->
|
||||
<!--
|
||||
<category name="org.alfresco">
|
||||
@@ -19,11 +14,6 @@
|
||||
</category>
|
||||
-->
|
||||
|
||||
<!-- ##### SOLR INFORMATION SERVER MESSAGES ##### -->
|
||||
<category name="org.alfresco.solr.SolrInformationServer">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<!-- ##### ONLY SOLR QUERIES ##### -->
|
||||
<!--
|
||||
<category name="org.apache.solr.core.SolrCore.Request">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</distributionManagement>
|
||||
|
||||
<properties>
|
||||
<dependency.alfresco-data-model.version>8.98</dependency.alfresco-data-model.version>
|
||||
<dependency.alfresco-data-model.version>8.105</dependency.alfresco-data-model.version>
|
||||
<dependency.jackson.version>2.10.3</dependency.jackson.version>
|
||||
</properties>
|
||||
|
||||
|
||||
+36
-36
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.solr;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class BoundedDeque<T> implements Iterable<T>
|
||||
public BoundedDeque(int max)
|
||||
{
|
||||
this.max = max;
|
||||
setDeque(new LinkedBlockingDeque<T>());
|
||||
this.deque = new LinkedBlockingDeque<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,21 +46,21 @@ public class BoundedDeque<T> implements Iterable<T>
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return getDeque().size();
|
||||
return deque.size();
|
||||
}
|
||||
|
||||
public void add(T add)
|
||||
{
|
||||
while (getDeque().size() > (max - 1))
|
||||
while (deque.size() > (max - 1))
|
||||
{
|
||||
getDeque().removeLast();
|
||||
deque.removeLast();
|
||||
}
|
||||
getDeque().addFirst(add);
|
||||
deque.addFirst(add);
|
||||
}
|
||||
|
||||
public T getLast()
|
||||
{
|
||||
return getDeque().getFirst();
|
||||
return deque.getFirst();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -70,17 +70,17 @@ public class BoundedDeque<T> implements Iterable<T>
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return getDeque().iterator();
|
||||
return deque.iterator();
|
||||
}
|
||||
|
||||
/** Get a copy of the deque. */
|
||||
public LinkedBlockingDeque<T> getDeque()
|
||||
{
|
||||
return deque;
|
||||
return new LinkedBlockingDeque(deque);
|
||||
}
|
||||
|
||||
public void setDeque(LinkedBlockingDeque<T> deque)
|
||||
{
|
||||
this.deque = deque;
|
||||
this.deque = new LinkedBlockingDeque(deque);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+54
-39
@@ -23,42 +23,57 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Andy
|
||||
*
|
||||
*/
|
||||
public class AclChangeSets
|
||||
{
|
||||
private List<AclChangeSet> aclChangeSets;
|
||||
|
||||
private Long maxChangeSetCommitTime;
|
||||
|
||||
private Long maxChangeSetId;
|
||||
|
||||
AclChangeSets(List<AclChangeSet> aclChangeSets, Long maxChangeSetCommitTime, Long maxChangeSetId)
|
||||
{
|
||||
this.aclChangeSets = aclChangeSets;
|
||||
this.maxChangeSetCommitTime = maxChangeSetCommitTime;
|
||||
this.maxChangeSetId = maxChangeSetId;
|
||||
}
|
||||
|
||||
public List<AclChangeSet> getAclChangeSets()
|
||||
{
|
||||
return aclChangeSets;
|
||||
}
|
||||
|
||||
public Long getMaxChangeSetCommitTime()
|
||||
{
|
||||
return maxChangeSetCommitTime;
|
||||
}
|
||||
|
||||
public Long getMaxChangeSetId()
|
||||
{
|
||||
return maxChangeSetId;
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Andy
|
||||
*
|
||||
*/
|
||||
public class AclChangeSets
|
||||
{
|
||||
private final List<AclChangeSet> aclChangeSets;
|
||||
|
||||
private Long maxChangeSetCommitTime;
|
||||
|
||||
private Long maxChangeSetId;
|
||||
|
||||
AclChangeSets(List<AclChangeSet> aclChangeSets, Long maxChangeSetCommitTime, Long maxChangeSetId)
|
||||
{
|
||||
this.aclChangeSets = (aclChangeSets == null ? null : new ArrayList<>(aclChangeSets));
|
||||
this.maxChangeSetCommitTime = maxChangeSetCommitTime;
|
||||
this.maxChangeSetId = maxChangeSetId;
|
||||
}
|
||||
|
||||
public AclChangeSets(List<AclChangeSet> aclChangeSets)
|
||||
{
|
||||
this.aclChangeSets = aclChangeSets;
|
||||
if (!aclChangeSets.isEmpty())
|
||||
{
|
||||
this.maxChangeSetCommitTime = aclChangeSets.stream()
|
||||
.max(Comparator.comparing(AclChangeSet::getCommitTimeMs)).get().getCommitTimeMs();
|
||||
this.maxChangeSetId = aclChangeSets.stream()
|
||||
.max(Comparator.comparing(AclChangeSet::getId)).get().getId();
|
||||
}
|
||||
}
|
||||
|
||||
public List<AclChangeSet> getAclChangeSets()
|
||||
{
|
||||
return Collections.unmodifiableList(aclChangeSets);
|
||||
}
|
||||
|
||||
public Long getMaxChangeSetCommitTime()
|
||||
{
|
||||
return maxChangeSetCommitTime;
|
||||
}
|
||||
|
||||
public Long getMaxChangeSetId()
|
||||
{
|
||||
return maxChangeSetId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-30
@@ -1,30 +1,33 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,7 +39,7 @@ public class AclReaders
|
||||
{
|
||||
private final long id;
|
||||
|
||||
private final List<String> readers;
|
||||
private List<String> readers;
|
||||
|
||||
private final List<String> denied;
|
||||
|
||||
@@ -47,8 +50,8 @@ public class AclReaders
|
||||
public AclReaders(long id, List<String> readers, List<String> denied, long aclChangeSetId, String tenantDomain)
|
||||
{
|
||||
this.id = id;
|
||||
this.readers = readers;
|
||||
this.denied = denied;
|
||||
this.readers = (readers == null ? null : new ArrayList<>(readers));
|
||||
this.denied = (denied == null ? null : new ArrayList<>(denied));
|
||||
this.aclChangeSetId = aclChangeSetId;
|
||||
this.tenantDomain = tenantDomain;
|
||||
}
|
||||
@@ -96,12 +99,17 @@ public class AclReaders
|
||||
|
||||
public List<String> getReaders()
|
||||
{
|
||||
return readers;
|
||||
return unmodifiableList(readers);
|
||||
}
|
||||
|
||||
public void setReaders(List readers)
|
||||
{
|
||||
this.readers = new ArrayList<>(readers);
|
||||
}
|
||||
|
||||
public List<String> getDenied()
|
||||
{
|
||||
return denied;
|
||||
return unmodifiableList(denied);
|
||||
}
|
||||
|
||||
public long getAclChangeSetId()
|
||||
|
||||
+29
-27
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
@@ -52,7 +52,8 @@ public class AlfrescoModel
|
||||
{
|
||||
return checksum;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
{
|
||||
if (this == other)
|
||||
@@ -70,7 +71,8 @@ public class AlfrescoModel
|
||||
checksum.equals(model.getChecksum()));
|
||||
}
|
||||
|
||||
public int hashcode()
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = 17;
|
||||
result = 31 * result + model.hashCode();
|
||||
|
||||
+32
-30
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
@@ -36,10 +36,10 @@ import org.alfresco.service.namespace.QName;
|
||||
*/
|
||||
public class AlfrescoModelDiff
|
||||
{
|
||||
public static enum TYPE
|
||||
public enum TYPE
|
||||
{
|
||||
NEW, CHANGED, REMOVED;
|
||||
};
|
||||
}
|
||||
|
||||
private QName modelName;
|
||||
private TYPE type;
|
||||
@@ -74,7 +74,8 @@ public class AlfrescoModelDiff
|
||||
{
|
||||
return newChecksum;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
{
|
||||
if(this == other)
|
||||
@@ -91,8 +92,9 @@ public class AlfrescoModelDiff
|
||||
diff.getOldChecksum().equals(getOldChecksum()) &&
|
||||
diff.getNewChecksum().equals(getNewChecksum()));
|
||||
}
|
||||
|
||||
public int hashcode()
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = 17;
|
||||
result = 31 * result + getModelName().hashCode();
|
||||
|
||||
+14
-1
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package org.alfresco.solr.client;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -44,7 +45,19 @@ public class Transactions
|
||||
this.transactions = transactions;
|
||||
this.maxTxnCommitTime = maxTxnCommitTime;
|
||||
this.maxTxnId = maxTxnId;
|
||||
}
|
||||
}
|
||||
|
||||
public Transactions(List<Transaction> transactions)
|
||||
{
|
||||
this.transactions = transactions;
|
||||
if (!transactions.isEmpty())
|
||||
{
|
||||
this.maxTxnCommitTime = transactions.stream()
|
||||
.max(Comparator.comparing(Transaction::getCommitTimeMs)).get().getCommitTimeMs();
|
||||
this.maxTxnId = transactions.stream()
|
||||
.max(Comparator.comparing(Transaction::getId)).get().getId();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Transaction> getTransactions()
|
||||
{
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Solr Client
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
package org.alfresco.solr;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/** Unit tests for the {@link BoundedDeque}. */
|
||||
public class BoundedDequeTest
|
||||
{
|
||||
/** Check that earlier entries are removed from the BoundedDeque. */
|
||||
@Test
|
||||
public void testBoundedness()
|
||||
{
|
||||
// Create a BoundedDeque with size two.
|
||||
BoundedDeque<Object> boundedDeque = new BoundedDeque<>(2);
|
||||
|
||||
// Add three things.
|
||||
boundedDeque.add("A");
|
||||
boundedDeque.add("B");
|
||||
boundedDeque.add("C");
|
||||
|
||||
// Check that the latest two are still there.
|
||||
LinkedBlockingDeque<Object> actual = boundedDeque.getDeque();
|
||||
assertEquals("Unexpected entries in BoundedDeque.", asList("C", "B"), new ArrayList(actual));
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,8 @@ services:
|
||||
- ALFRESCO_SECURE_COMMS=none
|
||||
#Enable Spellcheck by setting to true
|
||||
- ENABLE_SPELLCHECK=${SEARCH_ENABLE_SPELLCHECK}
|
||||
#Disable cascade tracking by setting to true, default false - as cascade tracking is enabled.
|
||||
- DISABLE_CASCADE_TRACKING=${DISABLE_CASCADE_TRACKING}
|
||||
ports:
|
||||
- 8083:8983 #Browser port
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Alfresco Search Services ${project.version} Docker Image
|
||||
|
||||
FROM alfresco/alfresco-base-java:11.0.1-openjdk-centos-7-72b88c6f1f4c
|
||||
FROM alfresco/alfresco-base-java:11.0.7-openjdk-centos-7-145f5d28c8ca
|
||||
LABEL creator="Gethin James" maintainer="Alfresco Search Services Team"
|
||||
|
||||
ENV DIST_DIR /opt/alfresco-search-services
|
||||
|
||||
@@ -101,4 +101,8 @@ if [[ true == "$ENABLE_SPELLCHECK" ]]; then
|
||||
sed -i 's/#alfresco.suggestable.property/alfresco.suggestable.property/' ${PWD}/solrhome/conf/shared.properties
|
||||
fi
|
||||
|
||||
if [[ true == "$DISABLE_CASCADE_TRACKING" ]]; then
|
||||
sed -i 's/alfresco.cascade.tracker.enabled=true/alfresco.cascade.tracker.enabled=false/' ${PWD}/solrhome/conf/shared.properties
|
||||
fi
|
||||
|
||||
bash -c "$@"
|
||||
Reference in New Issue
Block a user