REPO-1012 : DB builds: Fix a batch of tests that (regularly) fail on SQL Server

- adding retrying transactions for the propertyValueDAO.cleanupUnusedValues() method calls as MS SQL is slow and has problems coping with other threads updating the alf_prop_ tables
   - changed the order of the tests to help reduce other tests failing because of these clean up tests

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@129495 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Andrei Rebegea
2016-08-12 16:04:19 +00:00
parent 2f2ce963d7
commit a73174a971
3 changed files with 117 additions and 90 deletions

View File

@@ -60,13 +60,17 @@ import org.junit.runners.Suite;
LocaleDAOTest.class, LocaleDAOTest.class,
QNameDAOTest.class, QNameDAOTest.class,
PropertyValueDAOTest.class, PropertyValueDAOTest.class,
PropertyValueCleanupTest.class,
AuditDAOTest.class,
AppliedPatchDAOTest.class, AppliedPatchDAOTest.class,
AclCrudDAOTest.class, AclCrudDAOTest.class,
UsageDAOTest.class, UsageDAOTest.class,
SOLRDAOTest.class, SOLRDAOTest.class,
TenantAdminDAOTest.class, TenantAdminDAOTest.class,
// REOPO-1012 : run AuditDAOTest and PropertyValueCleanupTest near the end
// because their failure can cause other tests to fail on MS SQL
// AuditDAOTest fails if it runs after CannedQueryDAOTest so this order is a compromise
// CannedQueryDAOTest will fail on MS SQL if either AuditDAOTest or PropertyValueCleanupTest fail
PropertyValueCleanupTest.class,
AuditDAOTest.class,
CannedQueryDAOTest.class CannedQueryDAOTest.class
}) })
public class DomainTestSuite public class DomainTestSuite

View File

@@ -719,11 +719,11 @@ public class AuditDAOTest extends TestCase
if (dialect instanceof AlfrescoMySQLClusterNDBDialect) if (dialect instanceof AlfrescoMySQLClusterNDBDialect)
{ {
throw new Exception("TODO review this test case with NDB - note: throw exeception here else causes later tests to fail (when running via DomainTestSuite)"); throw new Exception("TODO review this test case with NDB - note: throw exeception here else causes later tests to fail (when running via DomainTestSuite)");
} }
// single test // single test
scriptCanDeleteOrphanedPropsWork(false); scriptCanDeleteOrphanedPropsWork(false);
} }
public void testMaxResults() throws Exception public void testMaxResults() throws Exception
{ {
@@ -804,8 +804,6 @@ public class AuditDAOTest extends TestCase
txn.commit(); txn.commit();
System.out.println("Created values for " + i + " entries in " + (System.currentTimeMillis() - startCreate) + " ms."); System.out.println("Created values for " + i + " entries in " + (System.currentTimeMillis() - startCreate) + " ms.");
txn = transactionService.getUserTransaction();
txn.begin();
if (!performance) if (!performance)
{ {
// Check there are some persisted values to delete. // Check there are some persisted values to delete.
@@ -826,9 +824,19 @@ public class AuditDAOTest extends TestCase
assertEquals(dateValue, propertyValueDAO.getPropertyValue(dateValue).getSecond()); assertEquals(dateValue, propertyValueDAO.getPropertyValue(dateValue).getSecond());
} }
} }
long startDelete = System.currentTimeMillis(); long startDelete = System.currentTimeMillis();
propertyValueDAO.cleanupUnusedValues(); RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
txn.commit(); {
public Void execute() throws Throwable
{
propertyValueDAO.cleanupUnusedValues();
return null;
}
};
// use a new transaction so it will retry in that transaction
txnHelper.doInTransaction(callback,false,true);
System.out.println("Cleaned values for " + i + " entries in " + (System.currentTimeMillis() - startDelete) + " ms."); System.out.println("Cleaned values for " + i + " entries in " + (System.currentTimeMillis() - startDelete) + " ms.");
if (!performance) if (!performance)

View File

@@ -33,7 +33,8 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.test_category.OwnJVMTestsCategory;
@@ -60,15 +61,12 @@ public class PropertyValueCleanupTest
private TransactionService transactionService; private TransactionService transactionService;
private AttributeService attributeService; private AttributeService attributeService;
private RetryingTransactionHelper txnHelper;
private PropertyValueDAO propertyValueDAO; private PropertyValueDAO propertyValueDAO;
@Before @Before
public void setUp() throws Exception public void setUp() throws Exception
{ {
transactionService = (TransactionService) ctx.getBean("TransactionService"); transactionService = (TransactionService) ctx.getBean("TransactionService");
txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setMaxRetries(0);
attributeService = (AttributeService) ctx.getBean("AttributeService"); attributeService = (AttributeService) ctx.getBean("AttributeService");
propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO"); propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO");
@@ -81,81 +79,98 @@ public class PropertyValueCleanupTest
{ {
((AbstractPropertyValueDAOImpl)propertyValueDAO).clearCaches(); ((AbstractPropertyValueDAOImpl)propertyValueDAO).clearCaches();
} }
@Test @Test
public synchronized void testRapidCreationDuringCleanup() throws Exception public synchronized void testRapidCreationDuringCleanup() throws Exception
{ {
// Create and delete some well-known attributes // Create and delete some well-known attributes
String toDeleteKey1 = "testRapidCreationDuringCleanup"; String toDeleteKey1 = "testRapidCreationDuringCleanup";
String toDeleteKey2 = UUID.randomUUID().toString(); String toDeleteKey2 = UUID.randomUUID().toString();
byte[] toDeleteProp = new String("Key is " + toDeleteKey2).getBytes("US-ASCII"); byte[] toDeleteProp = new String("Key is " + toDeleteKey2).getBytes("US-ASCII");
assertNull("Did not expect to find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2)); assertNull("Did not expect to find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2));
assertNull("Key2 should be NOT present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); assertNull("Key2 should be NOT present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2));
attributeService.createAttribute(toDeleteProp, toDeleteKey1, toDeleteKey2); attributeService.createAttribute(toDeleteProp, toDeleteKey1, toDeleteKey2);
assertNotNull("Did not find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2)); assertNotNull("Did not find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2));
// Check that we can get hold of the underlying property // Check that we can get hold of the underlying property
assertNotNull("Key2 should be present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); assertNotNull("Key2 should be present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2));
// Delete the attribute // Delete the attribute
attributeService.removeAttribute(toDeleteKey1, toDeleteKey2); attributeService.removeAttribute(toDeleteKey1, toDeleteKey2);
// Check that we can STILL get hold of the underlying property // Check that we can STILL get hold of the underlying property
assertNotNull("Key2 should be present as a property value (even if unreferenced)", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); assertNotNull("Key2 should be present as a property value (even if unreferenced)", propertyValueDAO.getPropertyStringValue(toDeleteKey2));
// Start threads that throw stuff into the AttributeService // Start threads that throw stuff into the AttributeService
ThreadGroup threadGroup = new ThreadGroup("testRapidCreationDuringCleanup"); ThreadGroup threadGroup = new ThreadGroup("testRapidCreationDuringCleanup");
InsertSerializableAttributes[] runnables = new InsertSerializableAttributes[2]; InsertSerializableAttributes[] runnables = new InsertSerializableAttributes[2];
for (int i = 0; i < runnables.length; i++)
{ try
// Runnable {
runnables[i] = new InsertSerializableAttributes(); for (int i = 0; i < runnables.length; i++)
// Put it in a thread {
String threadName = "" + i; // Runnable
Thread thread = new Thread(threadGroup, runnables[i], threadName); runnables[i] = new InsertSerializableAttributes();
thread.setDaemon(true); // Precautionary // Put it in a thread
// Start it String threadName = "" + i;
thread.start(); Thread thread = new Thread(threadGroup, runnables[i], threadName);
} thread.setDaemon(true); // Precautionary
// Start it
// Wait a bit for data to really get in there thread.start();
wait(1000L); }
// Now run the cleanup script // Wait a bit for data to really get in there
propertyValueDAO.cleanupUnusedValues(); wait(1000L);
// Stop the threads RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
for (int i = 0; i < runnables.length; i++) // do it in a retrying transaction because there may be other threads modifying
{ // the alf_prop_* and therefore the cleanup may fail sometimes
// Runnable RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
runnables[i].running.set(false); {
} public Void execute() throws Throwable
{
// Clear any caches // Now run the cleanup script
clearCaches(); propertyValueDAO.cleanupUnusedValues();
// The cleanup should have removed the key2 return null;
assertNull("Key2 should be NOT present as a property value (cleanup job)", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); }
};
// Now check that all the properties written can still be retrieved txnHelper.doInTransaction(callback);
for (int i = 0; i < runnables.length; i++) }
{ finally
// Runnable {
String key1 = runnables[i].key1; // Make sure we stop the Daemons
String key2 = runnables[i].key2; for (int i = 0; i < runnables.length; i++)
List<Integer> key3s = new ArrayList<Integer>(runnables[i].key3s); // Copy entire list {
for (Integer key3 : key3s) // Runnable
{ runnables[i].running.set(false);
// Get the attribute }
byte[] propFetched = (byte[]) attributeService.getAttribute(key1, key2, key3); }
if (propFetched == null) // Clear any caches
{ clearCaches();
// This is OK. As long as we don't get a failure
continue; // The cleanup should have removed the key2
} assertNull("Key2 should be NOT present as a property value (cleanup job)", propertyValueDAO.getPropertyStringValue(toDeleteKey2));
assertTrue(
"Arrays were not equal for " + key1 + ", " + key2 + ", " + key3, // Now check that all the properties written can still be retrieved
Arrays.equals(runnables[i].prop, propFetched)); for (int i = 0; i < runnables.length; i++)
} {
} // Runnable
} String key1 = runnables[i].key1;
String key2 = runnables[i].key2;
List<Integer> key3s = new ArrayList<Integer>(runnables[i].key3s); // Copy entire list
for (Integer key3 : key3s)
{
// Get the attribute
byte[] propFetched = (byte[]) attributeService.getAttribute(key1, key2, key3);
if (propFetched == null)
{
// This is OK. As long as we don't get a failure
continue;
}
assertTrue(
"Arrays were not equal for " + key1 + ", " + key2 + ", " + key3,
Arrays.equals(runnables[i].prop, propFetched));
}
}
}
/** /**
* Simple runnable that continuously creates new serializable attributes until stopped. * Simple runnable that continuously creates new serializable attributes until stopped.