#! /usr/bin/env perl use strict; use warnings; use utf8; use Hydra::Model::DB; STDOUT->autoflush(); STDERR->autoflush(1); binmode STDERR, ":encoding(utf8)"; my $db = Hydra::Model::DB->new(); my $vacuum = $db->storage->dbh->prepare("VACUUM;"); my $dryRun = defined $ENV{'HYDRA_DRY_RUN'}; my $batchSize = 10000; my $iterationsPerVacuum = 500; sub backfillJobsJobsetId { my ($skipLocked) = @_; my $logPrefix; if ($skipLocked) { $logPrefix = "(pass 1/2)"; } else { $logPrefix = "(pass 2/2)"; } print STDERR "$logPrefix Backfilling Jobs records where jobset_id is NULL...\n"; my $totalToGoSth = $db->storage->dbh->prepare(<<QUERY); SELECT COUNT(*) FROM jobs WHERE jobset_id IS NULL QUERY $totalToGoSth->execute(); my ($totalToGo) = $totalToGoSth->fetchrow_array; my $skipLockedStmt = $skipLocked ? "FOR UPDATE SKIP LOCKED" : ""; my $update10kJobs = $db->storage->dbh->prepare(<<QUERY); UPDATE jobs SET jobset_id = ( SELECT jobsets.id FROM jobsets WHERE jobsets.name = jobs.jobset AND jobsets.project = jobs.project ) WHERE (jobs.project, jobs.jobset, jobs.name) in ( SELECT jobsprime.project, jobsprime.jobset, jobsprime.name FROM jobs jobsprime WHERE jobsprime.jobset_id IS NULL $skipLockedStmt LIMIT ? ); QUERY print STDERR "$logPrefix Total Jobs records without a jobset_id: $totalToGo\n"; my $iteration = 0; my $affected; do { $iteration++; $affected = $update10kJobs->execute($batchSize); print STDERR "$logPrefix (batch #$iteration; $totalToGo remaining) Jobs.jobset_id: affected $affected rows...\n"; $totalToGo -= $affected; if ($iteration % $iterationsPerVacuum == 0) { print STDERR "$logPrefix (batch #$iteration) Vacuuming...\n"; $vacuum->execute(); } } while ($affected > 0); if ($skipLocked) { backfillJobsJobsetId(0); } } sub backfillBuildsJobsetId { my ($skipLocked) = @_; my $logPrefix; if ($skipLocked) { $logPrefix = "(pass 1/2)"; print STDERR "$logPrefix Backfilling unlocked Builds records where jobset_id is NULL...\n"; } else { $logPrefix = "(pass 2/2)"; print STDERR "$logPrefix Backfilling all Builds records where jobset_id is NULL...\n"; } my $skipLockedStmt = $skipLocked ? "FOR UPDATE SKIP LOCKED" : ""; my $update10kBuilds = $db->storage->dbh->prepare(<<"QUERY"); WITH updateprogress AS ( UPDATE builds SET jobset_id = ( SELECT jobsets.id FROM jobsets WHERE jobsets.name = builds.jobset AND jobsets.project = builds.project ) WHERE builds.id in ( SELECT buildprime.id FROM builds buildprime WHERE buildprime.jobset_id IS NULL AND buildprime.id >= ? ORDER BY buildprime.id $skipLockedStmt LIMIT ? ) RETURNING id ) SELECT count(*) AS affected, max(updateprogress.id) AS highest_id FROM updateprogress; QUERY my $lowestNullIdSth = $db->storage->dbh->prepare(<<QUERY); SELECT id FROM builds WHERE jobset_id IS NULL ORDER BY id LIMIT 1 QUERY $lowestNullIdSth->execute(); my ($highestId) = $lowestNullIdSth->fetchrow_array; my $totalToGoSth = $db->storage->dbh->prepare(<<QUERY); SELECT COUNT(*) FROM builds WHERE jobset_id IS NULL AND id >= ? QUERY $totalToGoSth->execute($highestId); my ($totalToGo) = $totalToGoSth->fetchrow_array; print STDERR "$logPrefix Total Builds records without a jobset_id: $totalToGo, starting at $highestId\n"; my $iteration = 0; my $affected; do { my $previousHighId = $highestId; $iteration++; $update10kBuilds->execute($highestId, $batchSize); ($affected, $highestId) = $update10kBuilds->fetchrow_array; print STDERR "$logPrefix (batch #$iteration; $totalToGo remaining) Builds.jobset_id: affected $affected rows; max ID: $previousHighId -> $highestId\n"; $totalToGo -= $affected; if ($iteration % $iterationsPerVacuum == 0) { print STDERR "$logPrefix (batch #$iteration) Vacuuming...\n"; $vacuum->execute(); } } while ($affected > 0); if ($skipLocked) { backfillBuildsJobsetId(0); } } die "syntax: $0\n" unless @ARGV == 0; print STDERR "Beginning with a VACUUM\n"; $vacuum->execute(); backfillJobsJobsetId(1); backfillBuildsJobsetId(1); print STDERR "Ending with a VACUUM\n"; $vacuum->execute();