Materialize the number of finished builds

The NrBuilds table tracks the value of ‘select count(*) from Builds
where finished = 0’, keeping it up to date via a trigger.  This is
necessary to make the /all page fast, since otherwise it needs to do a
sequential scan on the Builds table.
This commit is contained in:
Eelco Dolstra 2013-08-12 20:11:34 +02:00
parent 182f725612
commit 452c8e36d1
7 changed files with 136 additions and 8 deletions

View file

@ -56,13 +56,12 @@ sub all : Chained('get_builds') PathPart {
my $resultsPerPage = 20; my $resultsPerPage = 20;
my $nrBuilds = $c->stash->{allBuilds}->search({finished => 1})->count;
$c->stash->{baseUri} = $c->uri_for($self->action_for("all"), $c->req->captures); $c->stash->{baseUri} = $c->uri_for($self->action_for("all"), $c->req->captures);
$c->stash->{page} = $page; $c->stash->{page} = $page;
$c->stash->{resultsPerPage} = $resultsPerPage; $c->stash->{resultsPerPage} = $resultsPerPage;
$c->stash->{total} = $nrBuilds; $c->stash->{total} = $c->stash->{allBuilds}->search({finished => 1})->count
unless defined $c->stash->{total};
$c->stash->{builds} = [ $c->stash->{allBuilds}->search( $c->stash->{builds} = [ $c->stash->{allBuilds}->search(
{ finished => 1 }, { finished => 1 },

View file

@ -155,6 +155,7 @@ sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {
$c->stash->{allJobs} = $c->model('DB::Jobs'); $c->stash->{allJobs} = $c->model('DB::Jobs');
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceeded'); $c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceeded');
$c->stash->{channelBaseName} = "everything"; $c->stash->{channelBaseName} = "everything";
$c->stash->{total} = $c->model('DB::NrBuilds')->find('finished')->count;
} }

View file

@ -0,0 +1,75 @@
use utf8;
package Hydra::Schema::NrBuilds;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
=head1 NAME
Hydra::Schema::NrBuilds
=cut
use strict;
use warnings;
use base 'DBIx::Class::Core';
=head1 COMPONENTS LOADED
=over 4
=item * L<Hydra::Component::ToJSON>
=back
=cut
__PACKAGE__->load_components("+Hydra::Component::ToJSON");
=head1 TABLE: C<NrBuilds>
=cut
__PACKAGE__->table("NrBuilds");
=head1 ACCESSORS
=head2 what
data_type: 'text'
is_nullable: 0
=head2 count
data_type: 'integer'
is_nullable: 0
=cut
__PACKAGE__->add_columns(
"what",
{ data_type => "text", is_nullable => 0 },
"count",
{ data_type => "integer", is_nullable => 0 },
);
=head1 PRIMARY KEY
=over 4
=item * L</what>
=back
=cut
__PACKAGE__->set_primary_key("what");
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-08-12 17:59:18
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CK8eJGC803nGj0wnete9xg
# You can replace this text with custom code or comments, and it will be preserved on regeneration
1;

View file

@ -51,12 +51,12 @@ for (my $n = $schemaVersion; $n < $maxSchemaVersion; $n++) {
my @statements = $sql_splitter->split($schema); my @statements = $sql_splitter->split($schema);
eval { eval {
$dbh->begin_work; $dbh->begin_work;
sub run { sub run_ {
my ($stm) = @_; my ($stm) = @_;
print STDERR "executing SQL statement: $stm\n"; print STDERR "executing SQL statement: $stm\n";
$dbh->do($_); $dbh->do($_);
} }
run($_) foreach @statements; run_($_) foreach @statements;
$db->resultset('SchemaVersion')->update({version => $m}); $db->resultset('SchemaVersion')->update({version => $m});
$dbh->commit; $dbh->commit;
}; };

View file

@ -514,6 +514,36 @@ create table NewsItems (
); );
-- Cache of the number of finished builds.
create table NrBuilds (
what text primary key not null,
count integer not null
);
insert into NrBuilds(what, count) values('finished', 0);
#ifdef POSTGRESQL
create function modifyNrBuildsFinished() returns trigger as $$
begin
if ((tg_op = 'INSERT' and new.finished = 1) or
(tg_op = 'UPDATE' and old.finished = 0 and new.finished = 1)) then
update NrBuilds set count = count + 1 where what = 'finished';
elsif ((tg_op = 'DELETE' and old.finished = 1) or
(tg_op = 'UPDATE' and old.finished = 1 and new.finished = 0)) then
update NrBuilds set count = count - 1 where what = 'finished';
end if;
return null;
end;
$$ language plpgsql;
create trigger NrBuildsFinished after insert or update or delete on Builds
for each row
execute procedure modifyNrBuildsFinished();
#endif
-- Some indices. -- Some indices.
create index IndexBuildInputsOnBuild on BuildInputs(build); create index IndexBuildInputsOnBuild on BuildInputs(build);
@ -534,7 +564,7 @@ create index IndexBuildsOnJobAndSystem on Builds(project, jobset, job, system);
create index IndexBuildsOnJobset on Builds(project, jobset); create index IndexBuildsOnJobset on Builds(project, jobset);
create index IndexBuildsOnProject on Builds(project); create index IndexBuildsOnProject on Builds(project);
create index IndexBuildsOnTimestamp on Builds(timestamp); create index IndexBuildsOnTimestamp on Builds(timestamp);
create index IndexBuildsOnJobsetFinishedTimestamp on Builds(project, jobset, finished, timestamp DESC); create index IndexBuildsOnJobsetFinishedTimestamp on Builds(project, jobset, finished, timestamp DESC); -- obsolete?
create index IndexBuildsOnJobFinishedId on builds(project, jobset, job, system, finished, id DESC); create index IndexBuildsOnJobFinishedId on builds(project, jobset, job, system, finished, id DESC);
create index IndexBuildsOnJobSystemCurrent on Builds(project, jobset, job, system, isCurrent); create index IndexBuildsOnJobSystemCurrent on Builds(project, jobset, job, system, isCurrent);
create index IndexBuildsOnDrvPath on Builds(drvPath); create index IndexBuildsOnDrvPath on Builds(drvPath);

23
src/sql/upgrade-17.sql Normal file
View file

@ -0,0 +1,23 @@
create table NrBuilds (
what text primary key not null,
count integer not null
);
create function modifyNrBuildsFinished() returns trigger as $$
begin
if ((tg_op = 'INSERT' and new.finished = 1) or
(tg_op = 'UPDATE' and old.finished = 0 and new.finished = 1)) then
update NrBuilds set count = count + 1 where what = 'finished';
elsif ((tg_op = 'DELETE' and old.finished = 1) or
(tg_op = 'UPDATE' and old.finished = 1 and new.finished = 0)) then
update NrBuilds set count = count - 1 where what = 'finished';
end if;
return null;
end;
$$ language plpgsql;
create trigger NrBuildsFinished after insert or update or delete on Builds
for each row
execute procedure modifyNrBuildsFinished();
insert into NrBuilds(what, count) select 'finished', count(*) from Builds where finished = 1;

View file

@ -7,11 +7,11 @@ my $db = Hydra::Model::DB->new;
my @sources = $db->schema->sources; my @sources = $db->schema->sources;
my $nrtables = scalar(@sources); my $nrtables = scalar(@sources);
use Test::Simple tests => 43; use Test::Simple tests => 44;
foreach my $source (@sources) { foreach my $source (@sources) {
my $title = "Basic select query for $source"; my $title = "Basic select query for $source";
if ($source eq "SchemaVersion") { if ($source eq "SchemaVersion" || $source eq "NrBuilds") {
ok(scalar($db->resultset($source)->all) == 1, $title); ok(scalar($db->resultset($source)->all) == 1, $title);
} elsif( $source !~ m/^(LatestSucceeded|JobStatus|ActiveJobs)/) { } elsif( $source !~ m/^(LatestSucceeded|JobStatus|ActiveJobs)/) {
ok(scalar($db->resultset($source)->all) == 0, $title); ok(scalar($db->resultset($source)->all) == 0, $title);