Skip to content

Commit

Permalink
Chunk deleting of rows from the activity table
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Needham committed Feb 21, 2018
1 parent 76539af commit 11d97de
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 3 deletions.
23 changes: 20 additions & 3 deletions lib/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

namespace OCA\Activity;

use Doctrine\DBAL\Platforms\MySqlPlatform;
use OCA\Activity\Exception\InvalidFilterException;
use OCP\Activity\IEvent;
use OCP\Activity\IExtension;
Expand Down Expand Up @@ -406,8 +407,24 @@ public function deleteActivities($conditions) {
$sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList);
}

$query = $this->connection->prepare(
'DELETE FROM `*PREFIX*activity`' . $sqlWhere);
$query->execute($sqlParameters);
// Add galera safe delete chunking if using mysql
// Stops us hitting wsrep_max_ws_rows when large row counts are deleted
if($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
// Then use chunked delete
$max = 100000;
$query = $this->connection->prepare(
'DELETE FROM `*PREFIX*activity`' . $sqlWhere . " LIMIT " . $max);
do {
$query->execute($sqlParameters);
$deleted = $query->rowCount();
} while($deleted === $max);
} else {
// Dont use chunked delete - let the DB handle the large row count natively
$query = $this->connection->prepare(
'DELETE FROM `*PREFIX*activity`' . $sqlWhere);
$query->execute($sqlParameters);
}


}
}
43 changes: 43 additions & 0 deletions tests/DataDeleteActivitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
namespace OCA\Activity\Tests;

use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use OCA\Activity\Data;
use OCP\Activity\IExtension;

Expand Down Expand Up @@ -122,4 +124,45 @@ protected function assertTableKeys($expected, Statement $query, $keyName) {

$this->assertEquals($expected, $users);
}

public function testChunkingDeleteNotUsedWhenNotOnMysql() {
$ttl = (60 * 60 * 24 * max(1, 365));
$timelimit = time() - $ttl;
$activityManager = $this->createMock(\OCP\Activity\IManager::class);
$connection = $this->createMock(\OCP\IDBConnection::class);
$platform = $this->createMock(SqlitePlatform::class);
$connection->expects($this->once())->method('getDatabasePlatform')->willReturn($platform);

$statement = $this->createMock(Statement::class);
// Wont chunk
$statement->expects($this->exactly(0))->method('rowCount')->willReturnOnConsecutiveCalls(100000, 50);
$connection->expects($this->once())->method('prepare')->willReturn($statement);

$userSession = $this->createMock(\OCP\IUserSession::class);
$data = new Data($activityManager, $connection, $userSession);
$data->deleteActivities(array(
'timestamp' => array($timelimit, '<'),
));
}

public function testDeleteActivitiesIsChunkedOnMysql() {
$ttl = (60 * 60 * 24 * max(1, 365));
$timelimit = time() - $ttl;
$activityManager = $this->createMock(\OCP\Activity\IManager::class);
$connection = $this->createMock(\OCP\IDBConnection::class);
$platform = $this->createMock(MySqlPlatform::class);
$connection->expects($this->once())->method('getDatabasePlatform')->willReturn($platform);

$statement = $this->createMock(Statement::class);
// Will chunk
$statement->expects($this->exactly(2))->method('rowCount')->willReturnOnConsecutiveCalls(100000, 50);
$connection->expects($this->once())->method('prepare')->willReturn($statement);

$userSession = $this->createMock(\OCP\IUserSession::class);
$data = new Data($activityManager, $connection, $userSession);
$data->deleteActivities(array(
'timestamp' => array($timelimit, '<'),
));
}

}

0 comments on commit 11d97de

Please sign in to comment.