mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
PostgreSQL and MySQL fixes for ACE-5143: CLONE - CleanAlfPropTables deleting more than it should
- New test PropertyValueCleanupTest - Generates some data for the script to clean up - Concurrently generates attribute values while cleanup script is running - Checks that the script cleaned up the orphaned data - Checks all newly-generated attributes to ensure that they survived the script in tact - The second SQL statement modification can be reverted to produce the failure mode observed in ACE-5143 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@122471 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -27,6 +27,7 @@ import org.alfresco.repo.domain.mimetype.MimetypeDAOTest;
|
||||
import org.alfresco.repo.domain.node.NodeDAOTest;
|
||||
import org.alfresco.repo.domain.patch.AppliedPatchDAOTest;
|
||||
import org.alfresco.repo.domain.permissions.AclCrudDAOTest;
|
||||
import org.alfresco.repo.domain.propval.PropertyValueCleanupTest;
|
||||
import org.alfresco.repo.domain.propval.PropertyValueDAOTest;
|
||||
import org.alfresco.repo.domain.qname.QNameDAOTest;
|
||||
import org.alfresco.repo.domain.query.CannedQueryDAOTest;
|
||||
@@ -52,6 +53,7 @@ import org.junit.runners.Suite;
|
||||
LocaleDAOTest.class,
|
||||
QNameDAOTest.class,
|
||||
PropertyValueDAOTest.class,
|
||||
PropertyValueCleanupTest.class,
|
||||
AuditDAOTest.class,
|
||||
AppliedPatchDAOTest.class,
|
||||
AclCrudDAOTest.class,
|
||||
|
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.repo.domain.propval;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @see PropertyValueDAO#cleanupUnusedValues()
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 5.1
|
||||
*/
|
||||
@Category(OwnJVMTestsCategory.class)
|
||||
public class PropertyValueCleanupTest
|
||||
{
|
||||
private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
|
||||
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");
|
||||
|
||||
// Remove the caches to test all functionality
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
private void clearCaches()
|
||||
{
|
||||
((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;
|
||||
int maxKey3 = runnables[i].counter.get();
|
||||
for (int j = 0; j < maxKey3; j++)
|
||||
{
|
||||
Integer key3 = j;
|
||||
// Get the attribute
|
||||
byte[] propFetched = (byte[]) attributeService.getAttribute(key1, key2, key3);
|
||||
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.
|
||||
* Each thread has a unique second key value, a sequential third key and generates serializable (unshared)
|
||||
* property values.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
private class InsertSerializableAttributes implements Runnable
|
||||
{
|
||||
private final String key1;
|
||||
private final String key2;
|
||||
private final byte[] prop;
|
||||
|
||||
private AtomicBoolean running = new AtomicBoolean(true);
|
||||
private AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
private InsertSerializableAttributes() throws UnsupportedEncodingException
|
||||
{
|
||||
key1 = "PropertyValueCleanupTest";
|
||||
key2 = UUID.randomUUID().toString();
|
||||
prop = new String("Key is " + key2).getBytes("US-ASCII");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void run()
|
||||
{
|
||||
while (running.get())
|
||||
{
|
||||
Integer key3 = counter.get();
|
||||
attributeService.createAttribute(prop, key1, key2, key3);
|
||||
// Increment the counter
|
||||
counter.incrementAndGet();
|
||||
// Wait a bit so that we don't drown the test
|
||||
try { wait(10L); } catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.alfresco.repo.domain.propval;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -36,20 +34,26 @@ import javax.naming.CompositeName;
|
||||
import org.alfresco.repo.domain.propval.PropertyValueDAO.PropertyFinderCallback;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.extensions.surf.util.ISO8601DateFormat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @see PropertyValueDAO
|
||||
*
|
||||
@@ -68,8 +72,7 @@ public class PropertyValueDAOTest
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
transactionService = serviceRegistry.getTransactionService();
|
||||
transactionService = (TransactionService) ctx.getBean("TransactionService");
|
||||
txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
txnHelper.setMaxRetries(0);
|
||||
|
||||
|
Reference in New Issue
Block a user