mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-22 15:12:38 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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<Void> callback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -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<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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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<Void> callback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
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<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.
|
||||
|
||||
Reference in New Issue
Block a user