diff --git a/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java b/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java index f9c9696ef3..6a97d75440 100644 --- a/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java +++ b/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java @@ -60,13 +60,17 @@ import org.junit.runners.Suite; LocaleDAOTest.class, QNameDAOTest.class, PropertyValueDAOTest.class, - PropertyValueCleanupTest.class, - AuditDAOTest.class, AppliedPatchDAOTest.class, AclCrudDAOTest.class, UsageDAOTest.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 }) public class DomainTestSuite diff --git a/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java b/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java index f227e5c464..ec0724f330 100644 --- a/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java +++ b/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java @@ -719,11 +719,11 @@ public class AuditDAOTest extends TestCase 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)"); - } + } - // single test + // single test scriptCanDeleteOrphanedPropsWork(false); - } + } public void testMaxResults() throws Exception { @@ -804,8 +804,6 @@ public class AuditDAOTest extends TestCase txn.commit(); System.out.println("Created values for " + i + " entries in " + (System.currentTimeMillis() - startCreate) + " ms."); - txn = transactionService.getUserTransaction(); - txn.begin(); if (!performance) { // Check there are some persisted values to delete. @@ -826,9 +824,19 @@ public class AuditDAOTest extends TestCase assertEquals(dateValue, propertyValueDAO.getPropertyValue(dateValue).getSecond()); } } - long startDelete = System.currentTimeMillis(); - propertyValueDAO.cleanupUnusedValues(); - txn.commit(); + long startDelete = System.currentTimeMillis(); + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + 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."); if (!performance) diff --git a/source/test-java/org/alfresco/repo/domain/propval/PropertyValueCleanupTest.java b/source/test-java/org/alfresco/repo/domain/propval/PropertyValueCleanupTest.java index 36a8b0e4f1..cfb6098db3 100644 --- a/source/test-java/org/alfresco/repo/domain/propval/PropertyValueCleanupTest.java +++ b/source/test-java/org/alfresco/repo/domain/propval/PropertyValueCleanupTest.java @@ -33,7 +33,8 @@ import java.util.List; import java.util.UUID; 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.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; @@ -60,15 +61,12 @@ public class PropertyValueCleanupTest private TransactionService transactionService; private AttributeService attributeService; - private RetryingTransactionHelper txnHelper; private PropertyValueDAO propertyValueDAO; @Before public void setUp() throws Exception { transactionService = (TransactionService) ctx.getBean("TransactionService"); - txnHelper = transactionService.getRetryingTransactionHelper(); - txnHelper.setMaxRetries(0); attributeService = (AttributeService) ctx.getBean("AttributeService"); propertyValueDAO = (PropertyValueDAO) ctx.getBean("propertyValueDAO"); @@ -81,81 +79,98 @@ public class PropertyValueCleanupTest { ((AbstractPropertyValueDAOImpl)propertyValueDAO).clearCaches(); } - - @Test - public synchronized void testRapidCreationDuringCleanup() throws Exception - { - // Create and delete some well-known attributes - String toDeleteKey1 = "testRapidCreationDuringCleanup"; - String toDeleteKey2 = UUID.randomUUID().toString(); - byte[] toDeleteProp = new String("Key is " + toDeleteKey2).getBytes("US-ASCII"); - 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)); - attributeService.createAttribute(toDeleteProp, toDeleteKey1, toDeleteKey2); - assertNotNull("Did not find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2)); - // Check that we can get hold of the underlying property - assertNotNull("Key2 should be present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); - // Delete the attribute - attributeService.removeAttribute(toDeleteKey1, toDeleteKey2); - // 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)); - - // Start threads that throw stuff into the AttributeService - ThreadGroup threadGroup = new ThreadGroup("testRapidCreationDuringCleanup"); - InsertSerializableAttributes[] runnables = new InsertSerializableAttributes[2]; - for (int i = 0; i < runnables.length; i++) - { - // Runnable - runnables[i] = new InsertSerializableAttributes(); - // Put it in a thread - String threadName = "" + i; - Thread thread = new Thread(threadGroup, runnables[i], threadName); - thread.setDaemon(true); // Precautionary - // Start it - thread.start(); - } - - // Wait a bit for data to really get in there - wait(1000L); - - // Now run the cleanup script - propertyValueDAO.cleanupUnusedValues(); - - // Stop the threads - for (int i = 0; i < runnables.length; i++) - { - // Runnable - runnables[i].running.set(false); - } - - // Clear any caches - clearCaches(); - - // The cleanup should have removed the key2 - 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 - for (int i = 0; i < runnables.length; i++) - { - // Runnable - String key1 = runnables[i].key1; - String key2 = runnables[i].key2; - List key3s = new ArrayList(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)); - } - } - } + + @Test + public synchronized void testRapidCreationDuringCleanup() throws Exception + { + // Create and delete some well-known attributes + String toDeleteKey1 = "testRapidCreationDuringCleanup"; + String toDeleteKey2 = UUID.randomUUID().toString(); + byte[] toDeleteProp = new String("Key is " + toDeleteKey2).getBytes("US-ASCII"); + 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)); + attributeService.createAttribute(toDeleteProp, toDeleteKey1, toDeleteKey2); + assertNotNull("Did not find the attribute ", attributeService.getAttribute(toDeleteKey1, toDeleteKey2)); + // Check that we can get hold of the underlying property + assertNotNull("Key2 should be present as a property value", propertyValueDAO.getPropertyStringValue(toDeleteKey2)); + // Delete the attribute + attributeService.removeAttribute(toDeleteKey1, toDeleteKey2); + // 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)); + + // Start threads that throw stuff into the AttributeService + ThreadGroup threadGroup = new ThreadGroup("testRapidCreationDuringCleanup"); + InsertSerializableAttributes[] runnables = new InsertSerializableAttributes[2]; + + try + { + for (int i = 0; i < runnables.length; i++) + { + // Runnable + runnables[i] = new InsertSerializableAttributes(); + // Put it in a thread + String threadName = "" + i; + Thread thread = new Thread(threadGroup, runnables[i], threadName); + thread.setDaemon(true); // Precautionary + // Start it + thread.start(); + } + + // Wait a bit for data to really get in there + wait(1000L); + + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + // do it in a retrying transaction because there may be other threads modifying + // the alf_prop_* and therefore the cleanup may fail sometimes + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Now run the cleanup script + propertyValueDAO.cleanupUnusedValues(); + + return null; + } + }; + txnHelper.doInTransaction(callback); + } + finally + { + // Make sure we stop the Daemons + for (int i = 0; i < runnables.length; i++) + { + // Runnable + runnables[i].running.set(false); + } + } + // Clear any caches + clearCaches(); + + // The cleanup should have removed the key2 + 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 + for (int i = 0; i < runnables.length; i++) + { + // Runnable + String key1 = runnables[i].key1; + String key2 = runnables[i].key2; + List key3s = new ArrayList(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.