Skip to content

Commit

Permalink
fix(db): also chunk MariaDB deletes
Browse files Browse the repository at this point in the history
Signed-off-by: Anna Larch <anna@nextcloud.com>
  • Loading branch information
miaulalala committed Sep 12, 2023
1 parent 4594383 commit 2acf458
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 19 deletions.
4 changes: 3 additions & 1 deletion lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
use OCP\User\Events\UserDeletedEvent;
use OCP\Util;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

class Application extends App implements IBootstrap {
public const APP_ID = 'activity';
Expand Down Expand Up @@ -110,7 +111,8 @@ public function register(IRegistrationContext $context): void {
$context->registerService(Data::class, function (ContainerInterface $c) {
return new Data(
$c->get(IManager::class),
$c->get('ActivityConnectionAdapter')
$c->get('ActivityConnectionAdapter'),
$c->get(LoggerInterface::class),
);
});

Expand Down
84 changes: 66 additions & 18 deletions lib/Data.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
Expand Down Expand Up @@ -32,6 +34,7 @@
use OCP\Activity\IManager;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;

/**
* @brief Class for managing the data in the activities
Expand All @@ -48,14 +51,16 @@ class Data {

/** @var ?IQueryBuilder */
protected $insertMail;
private LoggerInterface $logger;

/**
* @param IManager $activityManager
* @param IDBConnection $connection
*/
public function __construct(IManager $activityManager, IDBConnection $connection) {
public function __construct(IManager $activityManager, IDBConnection $connection, LoggerInterface $logger) {
$this->activityManager = $activityManager;
$this->connection = $connection;
$this->logger = $logger;
}

/**
Expand Down Expand Up @@ -368,9 +373,16 @@ public function expire($expireDays = 365) {
* 'field' => 'value' => `field` = 'value'
* 'field' => array('value', 'operator') => `field` operator 'value'
*/
public function deleteActivities($conditions) {
$delete = $this->connection->getQueryBuilder();
$delete->delete('activity');
public function deleteActivities($conditions): void {
$platform = $this->connection->getDatabasePlatform();
if($platform instanceof MySQLPlatform) {
$this->logger->debug('Choosing chunked activity delete for MySQL/MariaDB', ['app' => 'activity']);
$this->deleteActivitiesForMySQL($conditions);
return;
}
$this->logger->debug('Choosing regular activity delete', ['app' => 'activity']);
$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('activity');

foreach ($conditions as $column => $comparison) {
if (is_array($comparison)) {
Expand All @@ -381,22 +393,14 @@ public function deleteActivities($conditions) {
$value = $comparison;
}

$delete->andWhere($delete->expr()->comparison($column, $operation, $delete->createNamedParameter($value)));
$deleteQuery->andWhere($deleteQuery->expr()->comparison($column, $operation, $deleteQuery->createNamedParameter($value)));
}

// 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;
$delete->setMaxResults($max);
do {
$deleted = $delete->executeStatement();
} while ($deleted === $max);
} else {
// Dont use chunked delete - let the DB handle the large row count natively
$delete->executeStatement();
}



// Dont use chunked delete - let the DB handle the large row count natively
$deleteQuery->executeStatement();
}

public function getById(int $activityId): ?IEvent {
Expand Down Expand Up @@ -467,4 +471,48 @@ public function getActivitySince(string $user, int $since, bool $byOthers) {

return $query->execute()->fetch();
}

/**
* Add galera safe delete chunking if using mysql
* Stops us hitting wsrep_max_ws_rows when large row counts are deleted
*
* @param array $conditions
* @return void
*/
private function deleteActivitiesForMySQL(array $conditions): void {
$query = $this->connection->getQueryBuilder();
$query->select('activity_id')
->from('activity');

foreach ($conditions as $column => $comparison) {
if (is_array($comparison)) {
$operation = $comparison[1] ?? '=';
$value = $comparison[0];
} else {
$operation = '=';
$value = $comparison;
}
$query->where($query->expr()->comparison($column, $operation, $query->createNamedParameter($value)));
}

$query->setMaxResults(10000);
$result = $query->executeQuery();
$count = $result->rowCount();
if($count === 0) {
return;
}
$ids = array_map(static function (array $id) {
return (int)$id[0];
}, $result->fetchAll(\PDO::FETCH_NUM));
$result->closeCursor();

$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('activity');
$deleteQuery->where($deleteQuery->expr()->in('activity_id', $deleteQuery->createParameter('ids'), IQueryBuilder::PARAM_INT_ARRAY));
$deleteQuery->setParameter('ids', $ids, IQueryBuilder::PARAM_INT_ARRAY);
$queryResult = $deleteQuery->executeStatement();
if($queryResult === 10000) {
$this->deleteActivitiesForMySQL($conditions);
}
}
}
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<UndefinedClass>
<errorLevel type="suppress">
<referencedClass name="Doctrine\DBAL\Platforms\MySQLPlatform" />
<referencedClass name="Doctrine\DBAL\Platforms\AbstractMySQLPlatform" />
<referencedClass name="Doctrine\DBAL\Types\Types" />
<referencedClass name="OC" />
<referencedClass name="OC\Core\Command\Base" />
Expand Down

0 comments on commit 2acf458

Please sign in to comment.