Squashed commit of the following:

commit 9edb082eeb0f4610c4f6a27273585631c1bd69d9
Author: Joel <joel.bernstein@alfresco.com>
Date:   Tue Apr 3 13:20:02 2018 -0400

    MNT-19180: AclTracker code clarification and remove commented debugging

commit 3109b3a3f26557e2f03725514d63f74aee18e044
Author: Michael <michael.suzuki@alfresco.com>
Date:   Fri Mar 30 16:48:28 2018 +0100

    MNT-19180: Fix intermittent failing test caused by the fix to tracker race condition.

commit fd9a4e5c0ae2a1785b7e239244a5ef07d4e376f0
Author: Joel <joel.bernstein@alfresco.com>
Date:   Sun Mar 25 21:37:04 2018 -0400

    MNT-19180: Add DistributedAlfrescoSolrTrackerRaceTest

commit d4ba8594742691c483a104ce47f2d5220b61bc58
Author: Joel <joel.bernstein@alfresco.com>
Date:   Tue Mar 20 21:05:42 2018 -0400

    MNT-19180: Fix race condition with TrackerState
This commit is contained in:
Joel
2018-04-03 14:06:18 -04:00
parent f2fb8f9739
commit 48a93e6ce4
5 changed files with 192 additions and 33 deletions
@@ -130,7 +130,42 @@ public abstract class AbstractTracker implements Tracker
* @throws Throwable
*/
protected abstract void doTrack() throws Throwable;
private boolean assertTrackerStateRemainsNull() {
/*
* This assertion is added to accommodate DistributedAlfrescoSolrTrackerRaceTest.
* The sleep is needed to allow the test case to add a txn into the queue before
* the tracker makes its call to pull transactions from the test repo client.
*/
try
{
Thread.sleep(5000);
}
catch(Exception e)
{
}
/*
* This ensures that getTrackerState does not have the side effect of setting the
* state instance variable. This allows classes outside of the tracker framework
* to safely call getTrackerState without interfering with the trackers design.
*/
getTrackerState();
if(state == null) {
return true;
} else {
return false;
}
}
/**
* Template method - subclasses must implement the {@link Tracker}-specific indexing
* by implementing the abstract method {@link #doTrack()}.
@@ -146,17 +181,26 @@ public abstract class AbstractTracker implements Tracker
try
{
runLock.acquire();
if(state==null && Boolean.parseBoolean(System.getProperty("alfresco.test", "false")))
{
assert(assertTrackerStateRemainsNull());
}
log.info("... Running " + this.getClass().getSimpleName() + " for core [" + coreName + "].");
if(state == null)
if(this.state == null)
{
getTrackerState();
state.setRunning(true);
/*
* Set the global state for the tracker here.
*/
this.state = getTrackerState();
this.state.setRunning(true);
}
else
{
continueState();
state.setRunning(true);
this.state.setRunning(true);
}
infoSrv.registerTrackerThread();
@@ -221,22 +265,21 @@ public abstract class AbstractTracker implements Tracker
state.incrementTrackerCycles();
}
public void invalidateState()
public synchronized void invalidateState()
{
state = null;
}
@Override
public TrackerState getTrackerState()
public synchronized TrackerState getTrackerState()
{
if(state != null)
if(this.state != null)
{
return state;
return this.state;
}
else
{
state = this.infoSrv.getTrackerInitialState();
return state;
return this.infoSrv.getTrackerInitialState();
}
}
@@ -642,16 +642,19 @@ public class AclTracker extends AbstractTracker
try
{
getWriteLock().acquire();
//System.out.println("############# AclTracker acquire lock ################");
TrackerState state = getTrackerState();
/*
* We acquire the tracker state again here and set it globally. This is because the
* 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();
Long fromCommitTime = getChangeSetFromCommitTime(changeSetsFound, state.getLastGoodChangeSetCommitTimeInIndex());
aclChangeSets = getSomeAclChangeSets(changeSetsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, 2000,
state.getTimeToStopIndexing());
//System.out.println("############# Changesets ################:"+aclChangeSets.getAclChangeSets().size());
setLastChangeSetIdAndCommitTimeInTrackerState(aclChangeSets, state);
@@ -104,6 +104,7 @@ public class MetadataTracker extends AbstractTracker implements Tracker
@Override
protected void doTrack() throws AuthenticationException, IOException, JSONException, EncoderException
{
// MetadataTracker must wait until ModelTracker has run
ModelTracker modelTracker = this.infoSrv.getAdminHandler().getTrackerRegistry().getModelTracker();
@@ -137,7 +138,6 @@ public class MetadataTracker extends AbstractTracker implements Tracker
private void trackRepository() throws IOException, AuthenticationException, JSONException, EncoderException
{
//System.out.println("########################### MetadataTracker.trackRepository ##########");
checkShutdown();
if(!isMaster && isSlave)
@@ -243,7 +243,6 @@ public class MetadataTracker extends AbstractTracker implements Tracker
Transaction firstTransaction = firstTransactions.getTransactions().get(0);
long firstTxId = firstTransaction.getId();
long firstTransactionCommitTime = firstTransaction.getCommitTimeMs();
//System.out.println("###### First txn:"+firstTxId+" : "+firstTransactionCommitTime);
int setSize = this.infoSrv.getTxDocsSize(""+firstTxId, ""+firstTransactionCommitTime);
if (setSize == 0)
@@ -575,7 +574,6 @@ public class MetadataTracker extends AbstractTracker implements Tracker
protected Transactions getSomeTransactions(BoundedDeque<Transaction> txnsFound, Long fromCommitTime, long timeStep,
int maxResults, long endTime) throws AuthenticationException, IOException, JSONException, EncoderException
{
//System.out.println("###################### getSomeTransactions ####### "+fromCommitTime+":"+endTime);
long actualTimeStep = timeStep;
@@ -593,7 +591,6 @@ public class MetadataTracker extends AbstractTracker implements Tracker
} while (((transactions.getTransactions().size() == 0) && (startTime < endTime))
|| ((transactions.getTransactions().size() > 0) && alreadyFoundTransactions(txnsFound, transactions)));
//System.out.println("############ done with transactions ######### " + startTime + ":" + endTime + ":" + transactions.getTransactions().size());
return transactions;
}
@@ -614,22 +611,20 @@ public class MetadataTracker extends AbstractTracker implements Tracker
try
{
getWriteLock().acquire();
//System.out.println("######## Metadata Tracket Acquiring Write Lock ########");
/*
* Check to see if we are using the capped router, and if the cap has already been set.
*/
/*
* We acquire the tracker state again here and set it globally. This is because the
* 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.
*/
TrackerState state = getTrackerState();
//System.out.println("######## Do Track Transactions ########:"+docCount);
this.state = getTrackerState();
Long fromCommitTime = getTxFromCommitTime(txnsFound, state.getLastGoodTxCommitTimeInIndex());
transactions = getSomeTransactions(txnsFound, fromCommitTime, TIME_STEP_1_HR_IN_MS, 2000,
state.getTimeToStopIndexing());
//System.out.println("######## get some! ########:"+transactions.getTransactions().size());
setLastTxCommitTimeAndTxIdInTrackerState(transactions, state);
@@ -645,17 +640,13 @@ public class MetadataTracker extends AbstractTracker implements Tracker
ArrayList<Transaction> txBatch = new ArrayList<>();
for (Transaction info : transactions.getTransactions()) {
//System.out.println("############## Transaction:"+ info.getId());
boolean isInIndex = (infoSrv.txnInIndex(info.getId(), true) && info.getCommitTimeMs() <= state.getLastIndexedTxCommitTime());
if (isInIndex) {
//System.out.println("################## Transaction Found In Index:"+info.getId());
txnsFound.add(info);
} else {
//System.out.println("################## New Transaction:"+info.getId());
// 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()) {
//System.out.println("################## We are upToDate:"+state.getTimeToStopIndexing());
upToDate = true;
break;
}
@@ -719,7 +710,6 @@ public class MetadataTracker extends AbstractTracker implements Tracker
finally
{
getWriteLock().release();
//System.out.println("######## Metadata Tracket Releasing Write Lock ########:"+getWriteLock().availablePermits());
}
}
while ((transactions.getTransactions().size() > 0) && (upToDate == false));
@@ -1,5 +1,6 @@
package org.alfresco.solr;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
import org.alfresco.solr.client.Node;
import org.alfresco.solr.client.NodeMetaData;
@@ -84,7 +85,7 @@ import static org.alfresco.solr.AlfrescoSolrUtils.createCoreUsingTemplate;
* @since solr 1.5
* @author Michael Suzuki
*/
@ThreadLeakLingering(linger = 2000)
@ThreadLeakLingering(linger = 5000)
public abstract class AbstractAlfrescoDistributedTest extends SolrTestCaseJ4
{
// TODO: this shouldn't be static. get the random when you need it to avoid
@@ -0,0 +1,122 @@
/*
* 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.alfresco.solr.AbstractAlfrescoDistributedTest;
import org.alfresco.repo.search.adaptor.lucene.QueryConstants;
import org.alfresco.solr.client.*;
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.lucene.util.LuceneTestCase;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.junit.Rule;
import org.junit.Test;
import static org.alfresco.solr.AlfrescoSolrUtils.*;
import static org.alfresco.solr.AlfrescoSolrUtils.getAclReaders;
import static org.alfresco.solr.AlfrescoSolrUtils.list;
import java.util.List;
import java.util.ArrayList;
/**
* @author Joel
*/
@SolrTestCaseJ4.SuppressSSL
@LuceneTestCase.SuppressCodecs({"Appending","Lucene3x","Lucene40","Lucene41","Lucene42","Lucene43", "Lucene44", "Lucene45","Lucene46","Lucene47","Lucene48","Lucene49"})
public class DistributedAlfrescoSolrTrackerRaceTest extends AbstractAlfrescoDistributedTest
{
@Rule
public JettyServerRule jetty = new JettyServerRule(2, this);
@Test
public void testTracker() throws Exception
{
putHandleDefaults();
AclChangeSet aclChangeSet = getAclChangeSet(1);
Acl acl = getAcl(aclChangeSet);
Acl acl2 = getAcl(aclChangeSet);
AclReaders aclReaders = getAclReaders(aclChangeSet, acl, list("joel"), list("phil"), null);
AclReaders aclReaders2 = getAclReaders(aclChangeSet, acl2, list("jim"), list("phil"), null);
Transaction txn = getTransaction(0, 2);
long txnCommitTimeMs = txn.getCommitTimeMs();
//Subtract from the commit time to go beyond hole retention
long backdatedCommitTimeMs = txnCommitTimeMs-(4600000);
txn.setCommitTimeMs(backdatedCommitTimeMs);
//Next create two nodes to update for the transaction
Node folderNode = getNode(txn, acl, Node.SolrApiNodeStatus.UPDATED);
Node fileNode = getNode(txn, acl, Node.SolrApiNodeStatus.UPDATED);
Node errorNode = getNode(txn, acl, Node.SolrApiNodeStatus.UPDATED);
//Next create the NodeMetaData for each node. TODO: Add more metadata
NodeMetaData folderMetaData = getNodeMetaData(folderNode, txn, acl, "mike", null, false);
NodeMetaData fileMetaData = getNodeMetaData(fileNode, txn, acl, "mike", ancestors(folderMetaData.getNodeRef()), false);
//The errorNodeMetaData will cause an exception.
NodeMetaData errorMetaData = getNodeMetaData(errorNode, txn, acl, "lisa", ancestors(folderMetaData.getNodeRef()), true);
//Index the transaction, nodes, and nodeMetaDatas.
//Note that the content is automatically created by the test framework.
indexTransaction(txn,
list(errorNode, folderNode, fileNode),
list(errorMetaData, folderMetaData, fileMetaData));
indexAclChangeSet(aclChangeSet,
list(acl, acl2),
list(aclReaders, aclReaders2));
System.out.println(backdatedCommitTimeMs + ":" + txnCommitTimeMs);
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, aclChangeSet.getId(), aclChangeSet.getId() + 1, true, false), BooleanClause.Occur.MUST));
BooleanQuery waitForQuery = builder.build();
waitForDocCountAllCores(waitForQuery, 1, 80000);
/*
* Query the index for the content
*/
//This acl should have one record in each core with DBID sharding
waitForDocCountAllCores(new TermQuery(new Term(QueryConstants.FIELD_READER, "jim")), 1, 80000);
waitForDocCount(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", "world")), 2, 100000);
waitForDocCount(new TermQuery(new Term("content@s___t@{http://www.alfresco.org/model/content/1.0}content", Long.toString(fileNode.getId()))), 1, 80000);
//This will run the query on the control client and the cluster and compare the result.
query(getDefaultTestClient(), true, "{\"locales\":[\"en\"], \"templates\": [{\"name\":\"t1\", \"template\":\"%cm:content\"}]}",
params("q", "t1:world", "qt", "/afts", "shards.qt", "/afts", "start", "0", "rows", "6", "sort", "id asc"));
}
}