Version1025Date20240308063933.php 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\DAV\Migration;
  8. use Closure;
  9. use OCP\AppFramework\Services\IAppConfig;
  10. use OCP\DB\ISchemaWrapper;
  11. use OCP\DB\QueryBuilder\IQueryBuilder;
  12. use OCP\DB\Types;
  13. use OCP\IDBConnection;
  14. use OCP\Migration\IOutput;
  15. use OCP\Migration\SimpleMigrationStep;
  16. class Version1025Date20240308063933 extends SimpleMigrationStep {
  17. public function __construct(
  18. private IAppConfig $appConfig,
  19. private IDBConnection $db,
  20. ) {
  21. }
  22. /**
  23. * @param IOutput $output
  24. * @param Closure(): ISchemaWrapper $schemaClosure
  25. * @param array $options
  26. * @return null|ISchemaWrapper
  27. */
  28. public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
  29. /** @var ISchemaWrapper $schema */
  30. $schema = $schemaClosure();
  31. foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
  32. $table = $schema->getTable($tableName);
  33. if (!$table->hasColumn('created_at')) {
  34. $table->addColumn('created_at', Types::INTEGER, [
  35. 'notnull' => true,
  36. 'length' => 4,
  37. 'default' => 0,
  38. ]);
  39. }
  40. }
  41. return $schema;
  42. }
  43. public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
  44. // The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob
  45. // but small enough to fit into a cluster transaction size.
  46. // For a 50k users instance that would still keep 10 changes on average.
  47. $limit = max(1, (int)$this->appConfig->getAppValue('totalNumberOfSyncTokensToKeep', '500000'));
  48. foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
  49. $thresholdSelect = $this->db->getQueryBuilder();
  50. $thresholdSelect->select('id')
  51. ->from($tableName)
  52. ->orderBy('id', 'desc')
  53. ->setFirstResult($limit)
  54. ->setMaxResults(1);
  55. $oldestIdResult = $thresholdSelect->executeQuery();
  56. $oldestId = $oldestIdResult->fetchColumn();
  57. $oldestIdResult->closeCursor();
  58. $qb = $this->db->getQueryBuilder();
  59. $update = $qb->update($tableName)
  60. ->set('created_at', $qb->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
  61. ->where(
  62. $qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)),
  63. );
  64. // If there is a lot of data we only set timestamp for the most recent rows
  65. // because the rest will be deleted by \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob
  66. // anyway.
  67. if ($oldestId !== false) {
  68. $update->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($oldestId, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
  69. }
  70. $updated = $update->executeStatement();
  71. $output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName);
  72. }
  73. }
  74. }