From 83f56aeb450e6d3e171f59385300b187bc999333 Mon Sep 17 00:00:00 2001 From: Anna Larch Date: Mon, 11 Sep 2023 21:30:30 +0200 Subject: [PATCH] fixup! fix(db): also chunk MariaDB deletes --- lib/Data.php | 86 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/lib/Data.php b/lib/Data.php index 46045d237..69114c569 100755 --- a/lib/Data.php +++ b/lib/Data.php @@ -1,4 +1,6 @@ activityManager = $activityManager; $this->connection = $connection; + $this->logger = $logger; } /** @@ -369,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)) { @@ -382,25 +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 - $platform = $this->connection->getDatabasePlatform(); - if ($platform instanceof MySQLPlatform) { - $logger = \OC::$server->get(LoggerInterface::class); - $logger->log('Chunking deletes for MySQLPlatform'); - // Then use chunked delete - $max = 10000; - $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 { @@ -471,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_STR_ARRAY)); + $deleteQuery->setParameter('ids', $ids, IQueryBuilder::PARAM_STR_ARRAY); + $queryResult = $deleteQuery->executeStatement(); + if($queryResult === 10000) { + $this->deleteActivitiesForMySQL($conditions); + } + } }