Add support for tracking custom metrics
[?]
Jul 30, 2015, 10:57 PM
T5BIOVJEMBIASP7EKQVV2N3VD6I56UXH6LCD5I33BDQEVHJAMGKQCDependencies
- [2]
LKEX7GZ4Show buildinput and buildproduct information in the Builds API - [3]
PCD3ZH6ZPartially revert 1c20cfdf2403feb78cef515faf15c04d5c9f17bd - [4]
BB2KXLXZMove the build time chart to the job page - [5]
WHULPA6SHandle failure with output - [6]
YHP5DSOOImprove parsing of hydra-build-products - [7]
OG3Z3QGCNamespace cleanup - [8]
MHVIT4JYSplit hydra-queue-runner.cc more - [9]
4S5JF5JPUse latest DBIx::Class::Schema::Loader - [10]
JIJDYWPYRemove the Build menu from the top bar - [11]
KSBB33REAdd a dashboard - [12]
SS4TZXNUDistinguish between permanent evaluation errors and transient input errors - [13]
QCGCX2BRGeneralize lazy tabs - [14]
ZVTSOVHN* Support Subversion checkouts. - [15]
K3HODXGHCheck all inputs for blame but only email selected inputs - [16]
YU6CND7CRemove support for views - [17]
W5OAZWPDDrop the errorMsg column in the Jobs table - [18]
KQAQ4FIFUpdate Schema classes - [19]
Y6AHH4THRemove the logfile and logSize columns from the database - [20]
24BMQDZAStart of single-process hydra-queue-runner - [21]
FHF6IZJQ* Basic release management: releases are now dynamically computed as - [22]
HJOEIMLRRefactor - [23]
D7PL2VWUMove more actions from the top bar - [24]
ECBA3GQO* Make the schema class names match the case of the SQL table names. - [25]
FTPCV25MStore aggregate members in the database - [26]
SJLEZFC4check getHydraPath in stead of Envvar HYDRA_DBI directly - [27]
GNIEG2GC* Disambiguate jobs by jobset name. I.e. jobs with the same name in - [28]
VJHIHMEH* Store the meta.longDescription and meta.license attributes in the - [29]
PCKLFRT5Support push notification of repository changes - [30]
4CELXP7PRemove the longDescription field - [31]
TX7Q4RASAdd page showing latest build steps - [32]
CVWVXXJUScale the Y axis to the visible points - [33]
AHTEIK7G* Added a maintainers field to the Builds table. - [34]
7ECJWNVXCleanup Project model - [35]
6BLUKEQ2* Caching of "path" inputs, and fake a revision number for those. - [36]
ZI535LI6* hydra: 'new' UI for project/jobset/job/build - [37]
SB2V735VKeep track of the database schema version - [38]
BHZXGT2H* Channels: provide an index page that lists all the packages in the - [39]
2GUAKGTBFix indentation of build.tt - [40]
BD3GRK4B* Get rid of "positive failures" and separate log phases. - [41]
3ZCEPLNO - [42]
QLOLZHRXAllow a per-jobset check interval - [43]
37R34XJO* Negative caching: don't perform a build if a dependency already - [44]
S5PV6IIM* Represent jobs explicitly in the DB. - [45]
IK53RV4V - [46]
B72GLND4 - [47]
H7CNGK4O* Log evaluation errors etc. in the DB. - [48]
L2E6EVE2* Merged the Build and Job tables. - [49]
3E6IP3R3* Add the name of the jobset to ReleaseSetJobs, otherwise we can't - [50]
S66BOMVU* Added authentication. - [51]
R5D7DZPE - [52]
V4RNHJNR* Add a link to each project's homepage. Suggested by karltk. - [53]
JTHWA6AMRename aggregate members to constituents - [54]
GEADFVZ5hydra-queue-runner: Improved scheduling - [55]
TLZ2SPBR - [56]
D3DIBMOK* For products that are directories (like manuals), allow a default - [57]
ZB3JV52WAdd a "My jobsets" tab to the dashboard - [58]
YTIDBFGUDrop unused "disabled" columns - [59]
BXHG3HYLWhen renaming a jobset, add a redirect from the old name - [60]
CMU3YKOU* Store the release name. - [61]
PMNWRTGJAdd multiple output support - [62]
RU7AQO7U* Role-based access control. Only admins can create projects. Only - [63]
6GZZDDQBMove the store path size chart to the job page - [64]
EYNG4EL4* Regenerate the bindings from a clean sqlite database. - [65]
LCKWLQW3* In Sqlite "release" is now a keyword, so use "release_" instead. - [66]
DH3KNBAVMerge remote-tracking branch 'upstream/who-broke-builds' into upstream-master - [67]
YTZOC7C5* Editing of jobset inputs. - [68]
LZVO64YGMerge in the first bits of the API work - [69]
G2T4WAHIStore the inputs of each evaluation in the database - [70]
X27GNHDV* Basic job info in the database. - [71]
TWVSALRL* Allow the maximum number of concurrent builds per platform to be - [72]
CLJQCY2X* Store info about all the build actions and allow them to be - [73]
SHBLLAVH* More global substitution. - [74]
KOTB7BKV - [75]
YAPITGB3* Boolean inputs. - [76]
TQKGQ5R3 - [*]
PQFOMNTLhydra-queue-runner: More stats - [*]
DEMSSSB2* Controller for jobs which inherits all actions in ListBuilds. So - [*]
D5QIOJGP* Move everything up one directory. - [*]
J5UVLXOK* Start of a basic Catalyst web interface. - [*]
ZIIXICG7Make the outputs' outpaths available via the Build JSON API - [*]
JM3DPYOMgenerated schema with new dbix class schema loader, grrrrrr - [*]
ZWCTAZGLadded newsitems, added some admin options to clear various caches. - [*]
ODNCGFQ5* Improved the navigation bar: don't include all projects (since that - [*]
XAJFR6SRAdd a chart to the job pages showing the closure size over time - [*]
HQ54SEMSAdd more spacing between products - [*]
N22GPKYT* Put info about logs / build products in the DB. - [*]
6QRHXIM3* Speed up the jobset index page. Especially the query to get the
Change contents
- edit in src/hydra-queue-runner/build-result.cc at line 8
static std::tuple<bool, string> secureRead(Path fileName){auto fail = std::make_tuple(false, "");if (!pathExists(fileName)) return fail;try {/* For security, resolve symlinks. */fileName = canonPath(fileName, true);if (!isInStore(fileName)) return fail;return std::make_tuple(true, readFile(fileName));} catch (Error & e) { return fail; }} - replacement in src/hydra-queue-runner/build-result.cc at line 58
Path productsFile = output + "/nix-support/hydra-build-products";if (!pathExists(productsFile)) continue;auto file = secureRead(output + "/nix-support/hydra-build-products");if (!std::get<0>(file)) continue; - replacement in src/hydra-queue-runner/build-result.cc at line 63[10.1339]→[10.1339:1385](∅→∅),[10.1385]→[6.326:440](∅→∅),[6.440]→[10.1439:1488](∅→∅),[10.1439]→[10.1439:1488](∅→∅),[10.1488]→[6.441:569](∅→∅),[6.569]→[10.1524:1525](∅→∅),[10.1524]→[10.1524:1525](∅→∅),[10.1525]→[6.570:640](∅→∅)
/* For security, resolve symlinks. */try {productsFile = canonPath(productsFile, true);} catch (Error & e) { continue; }if (!isInStore(productsFile)) continue;string contents;try {contents = readFile(productsFile);} catch (Error & e) { continue; }for (auto & line : tokenizeString<Strings>(contents, "\n")) {for (auto & line : tokenizeString<Strings>(std::get<1>(file), "\n")) { - edit in src/hydra-queue-runner/build-result.cc at line 130
/* Get metrics. */for (auto & output : outputs) {auto file = secureRead(output + "/nix-support/hydra-metrics");for (auto & line : tokenizeString<Strings>(std::get<1>(file), "\n")) {auto fields = tokenizeString<std::vector<std::string>>(line);if (fields.size() < 2) continue;BuildMetric metric;metric.name = fields[0]; // FIXME: validatemetric.value = atof(fields[1].c_str()); // FIXMEmetric.unit = fields.size() >= 3 ? fields[2] : "";res.metrics[metric.name] = metric;}} - edit in src/hydra-queue-runner/build-result.hh at line 16
};struct BuildMetric{std::string name, unit;double value; - edit in src/hydra-queue-runner/build-result.hh at line 36
std::map<std::string, BuildMetric> metrics; - edit in src/hydra-queue-runner/hydra-queue-runner.cc at line 241
for (auto & metric : res.metrics) {txn.parameterized("insert into BuildMetrics (build, name, unit, value, project, jobset, job, timestamp) values ($1, $2, $3, $4, $5, $6, $7, $8)")(build->id)(metric.second.name)(metric.second.unit, metric.second.unit != "")(metric.second.value)(build->projectName)(build->jobsetName)(build->jobName)(build->timestamp).exec();} - replacement in src/hydra-queue-runner/queue-monitor.cc at line 67
auto res = txn.parameterized("select id, project, jobset, job, drvPath, maxsilent, timeout from Builds where id > $1 and finished = 0 order by id")(lastBuildId).exec();auto res = txn.parameterized("select id, project, jobset, job, drvPath, maxsilent, timeout, timestamp from Builds where id > $1 and finished = 0 order by id")(lastBuildId).exec(); - replacement in src/hydra-queue-runner/queue-monitor.cc at line 79
build->fullJobName = row["project"].as<string>() + ":" + row["jobset"].as<string>() + ":" + row["job"].as<string>();build->projectName = row["project"].as<string>();build->jobsetName = row["jobset"].as<string>();build->jobName = row["job"].as<string>(); - edit in src/hydra-queue-runner/queue-monitor.cc at line 84
build->timestamp = row["timestamp"].as<time_t>(); - replacement in src/hydra-queue-runner/queue-monitor.cc at line 95
printMsg(lvlTalkative, format("loading build %1% (%2%)") % build->id % build->fullJobName);printMsg(lvlTalkative, format("loading build %1% (%2%)") % build->id % build->fullJobName()); - edit in src/hydra-queue-runner/state.hh at line 41
bssCachedFailure = 8, - replacement in src/hydra-queue-runner/state.hh at line 71
std::string fullJobName;std::string projectName, jobsetName, jobName;time_t timestamp; - edit in src/hydra-queue-runner/state.hh at line 78
std::string fullJobName(){return projectName + ":" + jobsetName + ":" + jobName;} - edit in src/lib/Hydra/Controller/Job.pm at line 80
$c->stash->{metrics} = [ $job->buildmetrics->search({ }, { select => ["name"], distinct => 1, order_by => "timestamp desc", }) ]; - edit in src/lib/Hydra/Controller/Job.pm at line 116
sub metric : Chained('job') PathPart('metric') Args(1) {my ($self, $c, $metricName) = @_;$c->stash->{template} = 'metric.tt';$c->stash->{metricName} = $metricName;my @res = $c->stash->{job}->buildmetrics->search({ name => $metricName },{ order_by => "timestamp", columns => [ "build", "name", "timestamp", "value", "unit" ] });$self->status_ok($c, entity => [ map { { id => $_->get_column("build"), timestamp => $_ ->timestamp, value => $_->value, unit => $_->unit } } @res ]);} - file addition: BuildMetrics.pm[80.477]
use utf8;package Hydra::Schema::BuildMetrics;# Created by DBIx::Class::Schema::Loader# DO NOT MODIFY THE FIRST PART OF THIS FILE=head1 NAMEHydra::Schema::BuildMetrics=cutuse 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<BuildMetrics>=cut__PACKAGE__->table("BuildMetrics");=head1 ACCESSORS=head2 builddata_type: 'integer'is_foreign_key: 1is_nullable: 0=head2 namedata_type: 'text'is_nullable: 0=head2 unitdata_type: 'text'is_nullable: 1=head2 valuedata_type: 'double precision'is_nullable: 0=head2 projectdata_type: 'text'is_foreign_key: 1is_nullable: 0=head2 jobsetdata_type: 'text'is_foreign_key: 1is_nullable: 0=head2 jobdata_type: 'text'is_foreign_key: 1is_nullable: 0=head2 timestampdata_type: 'integer'is_nullable: 0=cut__PACKAGE__->add_columns("build",{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },"name",{ data_type => "text", is_nullable => 0 },"unit",{ data_type => "text", is_nullable => 1 },"value",{ data_type => "double precision", is_nullable => 0 },"project",{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },"jobset",{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },"job",{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },"timestamp",{ data_type => "integer", is_nullable => 0 },);=head1 PRIMARY KEY=over 4=item * L</build>=item * L</name>=back=cut__PACKAGE__->set_primary_key("build", "name");=head1 RELATIONS=head2 buildType: belongs_toRelated object: L<Hydra::Schema::Builds>=cut__PACKAGE__->belongs_to("build","Hydra::Schema::Builds",{ id => "build" },{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },);=head2 jobType: belongs_toRelated object: L<Hydra::Schema::Jobs>=cut__PACKAGE__->belongs_to("job","Hydra::Schema::Jobs",{ jobset => "jobset", name => "job", project => "project" },{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },);=head2 jobsetType: belongs_toRelated object: L<Hydra::Schema::Jobsets>=cut__PACKAGE__->belongs_to("jobset","Hydra::Schema::Jobsets",{ name => "jobset", project => "project" },{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },);=head2 projectType: belongs_toRelated object: L<Hydra::Schema::Projects>=cut__PACKAGE__->belongs_to("project","Hydra::Schema::Projects",{ name => "project" },{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },);# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qoPm5/le+sVHigW4Dmum2Qsub json_hint {return { columns => ['value', 'unit'] };}1; - edit in src/lib/Hydra/Schema/Builds.pm at line 341
undef,);=head2 buildmetricsType: has_manyRelated object: L<Hydra::Schema::BuildMetrics>=cut__PACKAGE__->has_many("buildmetrics","Hydra::Schema::BuildMetrics",{ "foreign.build" => "self.id" }, - replacement in src/lib/Hydra/Schema/Builds.pm at line 553
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:03:55# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EwxiaQpqbdzI9RvU0uUtLQ# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Y2lDtgY8EBLOuCHAI8fWRQ - edit in src/lib/Hydra/Schema/Builds.pm at line 648
buildmetrics => 'name', - edit in src/lib/Hydra/Schema/Jobs.pm at line 83
=head2 buildmetricsType: has_manyRelated object: L<Hydra::Schema::BuildMetrics>=cut - edit in src/lib/Hydra/Schema/Jobs.pm at line 92
__PACKAGE__->has_many("buildmetrics","Hydra::Schema::BuildMetrics",{"foreign.job" => "self.name","foreign.jobset" => "self.jobset","foreign.project" => "self.project",},undef,); - replacement in src/lib/Hydra/Schema/Jobs.pm at line 172
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-09-29 19:41:42# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lnZSd0gDXgLk8WQeAFqByA# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vDAo9bzLca+QWfhOb9OLMg - edit in src/lib/Hydra/Schema/Jobsets.pm at line 186
=head2 buildmetricsType: has_manyRelated object: L<Hydra::Schema::BuildMetrics>=cut__PACKAGE__->has_many("buildmetrics","Hydra::Schema::BuildMetrics",{"foreign.jobset" => "self.name","foreign.project" => "self.project",},undef,); - replacement in src/lib/Hydra/Schema/Jobsets.pm at line 341
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-04-23 23:13:51# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CO0aE+jrjB+UrwGRzWZLlw# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Coci9FdBAvUO9T3st2NEqA - edit in src/lib/Hydra/Schema/Projects.pm at line 108
=head2 buildmetricsType: has_manyRelated object: L<Hydra::Schema::BuildMetrics> - edit in src/lib/Hydra/Schema/Projects.pm at line 115
=cut__PACKAGE__->has_many("buildmetrics","Hydra::Schema::BuildMetrics",{ "foreign.project" => "self.name" },undef,); - replacement in src/lib/Hydra/Schema/Projects.pm at line 285
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-04-23 23:13:08# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fkd9ruEoVSBGIktmAj4u4g# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:67kWIE0IGmEJTvOIATAKaw - replacement in src/root/build.tt at line 109
- edit in src/root/build.tt at line 388
[% IF build.finished && build.buildmetrics %]<h3>Metrics</h3><table class="table table-small table-striped table-hover clickable-rows"><thead><tr><th>Name</th><th>Value</th></tr></thead><tbody>[% FOREACH metric IN build.buildmetrics %]<tr><td><tt><a class="row-link" href="[% c.uri_for('/job' project.name jobset.name job.name 'metric' metric.name) %]">[%HTML.escape(metric.name)%]</a></tt></td><td>[%metric.value%][%metric.unit%]</td></tr>[% END %]</tbody></table>[% END %] - replacement in src/root/common.tt at line 569
var max = 0;var maxTime = 0;var minTime = Number.MAX_SAFE_INTEGER; - replacement in src/root/common.tt at line 575
max = Math.max(t, max);maxTime = Math.max(t, maxTime);minTime = Math.min(t, minTime); - replacement in src/root/common.tt at line 639
plot.setSelection({ xaxis: { from: max - 60 * 24 * 60 * 60 * 1000, to: max } });plot.setSelection({ xaxis: { from: Math.max(minTime, maxTime - 60 * 24 * 60 * 60 * 1000), to: maxTime } }); - edit in src/root/job.tt at line 100
[% FOREACH metric IN metrics %]<h3>Metric: <tt>[%HTML.escape(metric.name)%]</tt></h3>[% INCLUDE createChart id="metric-${metric.name}" dataUrl=c.uri_for('/job' project.name jobset.name job.name 'metric' metric.name) %][% END %] - file addition: metric.tt[80.1486]
[% WRAPPER layout.tt title="Job metric ‘$metricName’" %][% PROCESS common.tt %][% INCLUDE includeFlot %][% INCLUDE createChart id="chart" dataUrl=c.req.uri %][% END %] - edit in src/root/static/css/hydra.css at line 30
}table.table-small {width: auto !important; - edit in src/sql/hydra.sql at line 325
create table BuildMetrics (build integer not null,name text not null, - edit in src/sql/hydra.sql at line 331
unit text,value double precision not null, - edit in src/sql/hydra.sql at line 334
-- Denormalisation for performance: copy some columns from the-- corresponding build.project text not null,jobset text not null,job text not null,timestamp integer not null,primary key (build, name),foreign key (build) references Builds(id) on delete cascade,foreign key (project) references Projects(name) on update cascade,foreign key (project, jobset) references Jobsets(project, name) on update cascade,foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade); - edit in src/sql/hydra.sql at line 615[89.2243][89.2243]
create index IndexBuildMetricsOnJobTimestamp on BuildMetrics(project, jobset, job, timestamp desc); - file addition: upgrade-39.sql[80.3004]
create table BuildMetrics (build integer not null,name text not null,unit text,value double precision not null,-- Denormalisation for performance: copy some columns from the-- corresponding build.project text not null,jobset text not null,job text not null,timestamp integer not null,primary key (build, name),foreign key (build) references Builds(id) on delete cascade,foreign key (project) references Projects(name) on update cascade,foreign key (project, jobset) references Jobsets(project, name) on update cascade,foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade);create index IndexBuildMetricsOnJobTimestamp on BuildMetrics(project, jobset, job, timestamp desc);