Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).

## [5.1.5] - 2026-06-22
### Added
* *Nothing*

### Changed
* *Nothing*

### Deprecated
* *Nothing*

### Removed
* *Nothing*

### Fixed
* [#2634](https://github.com/shlinkio/shlink/issues/2634) Improve performance of Version20260607082210 migration by fetching short URLs in batches.


## [5.1.4] - 2026-06-18
### Added
* *Nothing*
Expand Down
55 changes: 29 additions & 26 deletions module/Core/migrations/Version20260607082210.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
final class Version20260607082210 extends AbstractMigration
{
public function up(Schema $schema): void
{
do {
$resultsFound = $this->processBatch();
} while ($resultsFound);
}

public function processBatch(): bool
{
$qb = $this->connection->createQueryBuilder();
$qb
Expand All @@ -25,37 +32,33 @@ public function up(Schema $schema): void
// If this migration times out, this will ensure it can be rerun, and it will continue where it was left, so
// it can be run multiple times until all short URLs have been processed
->where($qb->expr()->eq('long_url_hash', ':longUrlHash'))
->setParameters(['longUrlHash' => '']);
->setParameters(['longUrlHash' => ''])
->setMaxResults(10_000);
$shortUrlsResult = $qb->executeQuery();

$iteration = 1;
$this->connection->beginTransaction();
return $this->connection->transactional(function () use ($shortUrlsResult) {
$resultsFound = false;

while ($row = $shortUrlsResult->fetchAssociative()) {
// Every few updates, commit the transaction and begin a new one
if (($iteration % 10_000) === 0) {
$this->connection->commit();
$this->connection->beginTransaction();
}
$iteration++;
while ($row = $shortUrlsResult->fetchAssociative()) {
$resultsFound = true;

$updateQb = $this->connection->createQueryBuilder();
$updateQb
->update('short_urls')
->set('long_url_hash', ':binHash')
->where($updateQb->expr()->eq('id', ':id'))
->setParameters([
'id' => $row['id'],
'binHash' => hex2bin(hash('sha256', $row['original_url'])),
], [
'binHash' => Types::BINARY,
])
->setMaxResults(1)
->executeStatement();
}
$updateQb = $this->connection->createQueryBuilder();
$updateQb
->update('short_urls')
->set('long_url_hash', ':binHash')
->where($updateQb->expr()->eq('id', ':id'))
->setParameters([
'id' => $row['id'],
'binHash' => hex2bin(hash('sha256', $row['original_url'])),
], [
'binHash' => Types::BINARY,
])
->setMaxResults(1)
->executeStatement();
}

// Commit any pending update that is still pending
$this->connection->commit();
return $resultsFound;
});
}

public function isTransactional(): bool
Expand Down
Loading