From ca153067211627183307190d1ec55f0f255fa072 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Wed, 19 Jun 2024 12:30:32 +0200 Subject: [PATCH] fix(dav): Limit number of UPDATES for sync token created_at Address book and calendar sync tokens have a created_at column in 26+ and we need to assign a current timestamp to the existing data at upgrade so the data isn't cleaned up immediately. Updating the full table is expensive and fails on clustered setups that limit transaction size. We don't need a timestamp for the oldest rows so we can skip updating them. Signed-off-by: Christoph Wurst --- .../Version1025Date20240308063933.php | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/apps/dav/lib/Migration/Version1025Date20240308063933.php b/apps/dav/lib/Migration/Version1025Date20240308063933.php index a75fc85eccc39..e30a9ea02921f 100644 --- a/apps/dav/lib/Migration/Version1025Date20240308063933.php +++ b/apps/dav/lib/Migration/Version1025Date20240308063933.php @@ -27,6 +27,7 @@ namespace OCA\DAV\Migration; use Closure; +use OCP\AppFramework\Services\IAppConfig; use OCP\DB\ISchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\Types; @@ -36,10 +37,13 @@ class Version1025Date20240308063933 extends SimpleMigrationStep { + private IAppConfig $appConfig; private IDBConnection $db; - public function __construct(IDBConnection $db) { + public function __construct(IAppConfig $appConfig, + IDBConnection $db) { $this->db = $db; + $this->appConfig = $appConfig; } /** @@ -67,7 +71,22 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void { + // The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob + // but small enough to fit into a cluster transaction size. + // For a 50k users instance that would still keep 10 changes on average. + $limit = max(1, (int) $this->appConfig->getAppValue('totalNumberOfSyncTokensToKeep', '500000')); + foreach (['addressbookchanges', 'calendarchanges'] as $tableName) { + $thresholdSelect = $this->db->getQueryBuilder(); + $thresholdSelect->select('id') + ->from($tableName) + ->orderBy('id', 'desc') + ->setFirstResult($limit) + ->setMaxResults(1); + $oldestIdResult = $thresholdSelect->executeQuery(); + $oldestId = $oldestIdResult->fetchColumn(); + $oldestIdResult->closeCursor(); + $qb = $this->db->getQueryBuilder(); $update = $qb->update($tableName) @@ -76,7 +95,15 @@ public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)), ); + // If there is a lot of data we only set timestamp for the most recent rows + // because the rest will be deleted by \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob + // anyway. + if ($oldestId !== false) { + $update->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($oldestId, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); + } + $updated = $update->executeStatement(); + $output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName); } }