diff --git a/src/main/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java b/src/main/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
index 2ae2c3b711..f32d5edc4e 100644
--- a/src/main/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
+++ b/src/main/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
@@ -1,28 +1,28 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
- * provided under the following open source license terms:
- *
- * 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 .
- * #L%
- */
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * 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 .
+ * #L%
+ */
package org.alfresco.repo.node.db;
import java.util.ArrayList;
@@ -43,6 +43,8 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti
public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
{
private long minPurgeAgeMs;
+ // used for tests, to consider only transactions after a certain commit time
+ private long fromCustomCommitTime = -1;
// Unused transactions will be purged in chunks determined by commit time boundaries. 'index.tracking.purgeSize' specifies the size
// of the chunk (in ms). Default is a couple of hours.
@@ -88,6 +90,16 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
this.minPurgeAgeMs = ((long) minPurgeAgeDays) * 24L * 3600L * 1000L;
}
+ /**
+ * Set a custom "from commit time" that will consider only the transactions after this specified time
+ * Setting a negative value or 0 will trigger the default behaviour to get the oldest "from time" for any deleted node
+ *
+ * @param fromCustomCommitTime the custom from commit time value
+ */
+ public void setFromCustomCommitTime(long fromCustomCommitTime)
+ {
+ this.fromCustomCommitTime = fromCustomCommitTime;
+ }
/**
* Set the purge transaction block size. This determines how many unused transactions are purged in one go.
*
@@ -109,8 +121,11 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
final List results = new ArrayList(100);
final long maxCommitTime = System.currentTimeMillis() - minAge;
- long fromCommitTime = nodeDAO.getMinTxnCommitTimeForDeletedNodes().longValue();
-
+ long fromCommitTime = fromCustomCommitTime;
+ if (fromCommitTime <= 0L)
+ {
+ fromCommitTime = nodeDAO.getMinTxnCommitTimeForDeletedNodes().longValue();
+ }
if ( fromCommitTime == 0L )
{
String msg = "There are no old nodes to purge.";
@@ -225,8 +240,11 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
final List results = new ArrayList(100);
final long maxCommitTime = System.currentTimeMillis() - minAge;
- long fromCommitTime = nodeDAO.getMinUnusedTxnCommitTime().longValue();
-
+ long fromCommitTime = fromCustomCommitTime;
+ if (fromCommitTime <= 0L)
+ {
+ fromCommitTime = nodeDAO.getMinUnusedTxnCommitTime().longValue();
+ }
// delete unused transactions in batches of size 'purgeTxnBlockSize'
while (true)
{
diff --git a/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-delete-SqlMap.xml b/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-delete-SqlMap.xml
index ddeb6536f8..b6d00c10a7 100644
--- a/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-delete-SqlMap.xml
+++ b/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-delete-SqlMap.xml
@@ -9,9 +9,9 @@
delete from alf_node
where
type_qname_id = #{typeQNameId} and
- transaction_id <=
+ transaction_id IN
(
- select max(txn.id) from alf_transaction txn
+ select txn.id from alf_transaction txn
where
txn.commit_time_ms >= #{minCommitTime} and
txn.commit_time_ms < #{maxCommitTime}
diff --git a/src/test/java/org/alfresco/AllDBTestsTestSuite.java b/src/test/java/org/alfresco/AllDBTestsTestSuite.java
index dbec64d7cc..a43033e3b9 100644
--- a/src/test/java/org/alfresco/AllDBTestsTestSuite.java
+++ b/src/test/java/org/alfresco/AllDBTestsTestSuite.java
@@ -79,6 +79,8 @@ import org.junit.runners.Suite;
// REPO-2963 : Tests causing a cascade of failures in AllDBTestsTestSuite on PostgreSQL/MySQL
// Moved at the bottom of the suite because DbNodeServiceImplTest.testNodeCleanupRegistry() takes a long time on a clean DB.
org.alfresco.repo.node.db.DbNodeServiceImplTest.class,
+
+ org.alfresco.repo.node.cleanup.TransactionCleanupTest.class
})
public class AllDBTestsTestSuite
{
diff --git a/src/test/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java b/src/test/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java
index b94f046860..6329bea625 100644
--- a/src/test/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java
+++ b/src/test/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java
@@ -57,7 +57,7 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
-import org.alfresco.util.testing.category.LuceneTests;
+import org.alfresco.util.testing.category.DBTests;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
@@ -71,12 +71,12 @@ import org.springframework.extensions.webscripts.GUID;
* @author Derek Hulley
* @since 4.0
*/
-@Category({OwnJVMTestsCategory.class, LuceneTests.class})
+@Category({OwnJVMTestsCategory.class, DBTests.class })
public class TransactionCleanupTest
{
private static Log logger = LogFactory.getLog(TransactionCleanupTest.class);
- private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+ private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private NodeService nodeService;
@@ -93,6 +93,8 @@ public class TransactionCleanupTest
private NodeRef nodeRef5;
private RetryingTransactionHelper helper;
+ private long fromCustomCommitTime = -1;
+
@SuppressWarnings("unchecked")
@Before
public void before()
@@ -175,7 +177,35 @@ public class TransactionCleanupTest
return txnIds;
}
-
+
+ private Map> createTransactionsForNodePurgeTest()
+ {
+ Map> txnIds = new HashMap>();
+ DeleteNode deleteNode4 = new DeleteNode(nodeRef4);
+ DeleteNode deleteNode5 = new DeleteNode(nodeRef5);
+ List txnIds4 = new ArrayList();
+ List txnIds5 = new ArrayList();
+ txnIds.put(nodeRef4, txnIds4);
+ txnIds.put(nodeRef5, txnIds5);
+
+ String txnId4 = helper.doInTransaction(deleteNode4, false, true);
+ txnIds4.add(txnId4);
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (Exception e)
+ {
+ // not a problem
+ }
+ fromCustomCommitTime = System.currentTimeMillis();
+
+ String txnId5 = helper.doInTransaction(deleteNode5, false, true);
+ txnIds5.add(txnId5);
+
+ return txnIds;
+ }
+
private boolean containsTransaction(List txns, String txnId)
{
boolean found = false;
@@ -295,7 +325,40 @@ public class TransactionCleanupTest
assertNull("Node 4 was not cleaned up", nodeDAO.getNodeRefStatus(nodeRef4));
assertNull("Node 5 was not cleaned up", nodeDAO.getNodeRefStatus(nodeRef5));
}
-
+
+ @Test public void testPurgeNodeUseTransactionCommitTime() throws Exception
+ {
+ // make sure we clean up all the other nodes that may require purging
+ worker.setPurgeSize(7200000);// 2 hours
+ worker.doClean();
+ // delete the node 4 and node 5 with a half a second delay between the events
+ createTransactionsForNodePurgeTest();
+
+ // Double-check that n4 and n5 are present in deleted form
+ nodesCache.clear();
+
+ assertNotNull("Node 4 is deleted but not purged", nodeDAO.getNodeRefStatus(nodeRef4));
+ assertNotNull("Node 5 is deleted but not purged", nodeDAO.getNodeRefStatus(nodeRef5));
+
+ // run the transaction cleaner
+ worker.setPurgeSize(5); // small purge size
+ // we want to clean all the transactions starting with the fromCustomCommitTime
+ worker.setFromCustomCommitTime(fromCustomCommitTime);
+ List reports = worker.doClean();
+ for (String report : reports)
+ {
+ logger.debug(report);
+ }
+
+ // only node 5 should be purged,
+ // node 4 should still be present as the transaction happened before fromCustomCommitTime
+ nodesCache.clear();
+
+ assertNotNull("Node 4 is deleted but not purged", nodeDAO.getNodeRefStatus(nodeRef4));
+
+ assertNull("Node 5 was not cleaned up", nodeDAO.getNodeRefStatus(nodeRef5));
+ }
+
@After
public void after()
{