<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[203486] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/203486">203486</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-07-20 20:25:59 -0700 (Wed, 20 Jul 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Perf dashboard always re-generate measurement set JSON
https://bugs.webkit.org/show_bug.cgi?id=159951

Reviewed by Chris Dumez.

The bug was caused by manifest.json reporting the last modified date of a measurement set in floating point,
and a measurement set JSON reporting it as an integer. Fixed the bug by always using an integer.

* public/api/measurement-set.php:
(main): Return 404 when the results is empty.
(MeasurementSetFetcher::execute_query): Use &quot;extract(epoch from commit_time)&quot; like ManifestGenerator to improve
the generation speed. This is ~10% runtime improvement.
(MeasurementSetFetcher::format_map): Updated to reflect the above change.
(MeasurementSetFetcher::parse_revisions_array): Ditto.

* public/include/manifest.php:
(ManifestGenerator::platforms): Fixed the bug by coercing lastModified to integer (instead of float).

* server-tests/api-measurement-set-tests.js: Added a test case for returning empty results, and a test case for
making sure lastModified dates in manifest.json and measurement sets match.

* tools/js/remote.js:
(RemoteAPI.prototype.sendHttpRequest): Reject the promise when HTTP status code is not 200.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicapimeasurementsetphp">trunk/Websites/perf.webkit.org/public/api/measurement-set.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicincludemanifestphp">trunk/Websites/perf.webkit.org/public/include/manifest.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgservertestsapimeasurementsettestsjs">trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgtoolsjsremotejs">trunk/Websites/perf.webkit.org/tools/js/remote.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (203485 => 203486)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2016-07-21 02:09:51 UTC (rev 203485)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2016-07-21 03:25:59 UTC (rev 203486)
</span><span class="lines">@@ -1,3 +1,29 @@
</span><ins>+2016-07-19  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        Perf dashboard always re-generate measurement set JSON
+        https://bugs.webkit.org/show_bug.cgi?id=159951
+
+        Reviewed by Chris Dumez.
+
+        The bug was caused by manifest.json reporting the last modified date of a measurement set in floating point,
+        and a measurement set JSON reporting it as an integer. Fixed the bug by always using an integer.
+
+        * public/api/measurement-set.php:
+        (main): Return 404 when the results is empty.
+        (MeasurementSetFetcher::execute_query): Use &quot;extract(epoch from commit_time)&quot; like ManifestGenerator to improve
+        the generation speed. This is ~10% runtime improvement.
+        (MeasurementSetFetcher::format_map): Updated to reflect the above change.
+        (MeasurementSetFetcher::parse_revisions_array): Ditto.
+
+        * public/include/manifest.php:
+        (ManifestGenerator::platforms): Fixed the bug by coercing lastModified to integer (instead of float).
+
+        * server-tests/api-measurement-set-tests.js: Added a test case for returning empty results, and a test case for
+        making sure lastModified dates in manifest.json and measurement sets match.
+
+        * tools/js/remote.js:
+        (RemoteAPI.prototype.sendHttpRequest): Reject the promise when HTTP status code is not 200.
+
</ins><span class="cx"> 2016-07-09  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Perf dashboard can consume 50-70% of CPU on MacBook even if user is not interacting at all
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicapimeasurementsetphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/api/measurement-set.php (203485 => 203486)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/api/measurement-set.php        2016-07-21 02:09:51 UTC (rev 203485)
+++ trunk/Websites/perf.webkit.org/public/api/measurement-set.php        2016-07-21 03:25:59 UTC (rev 203486)
</span><span class="lines">@@ -31,6 +31,11 @@
</span><span class="cx">             array('platform' =&gt; $platform_id, 'metric' =&gt; $metric_id));
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if ($fetcher-&gt;at_end()) {
+        header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+        exit(404);
+    }
+
</ins><span class="cx">     $cluster_count = 0;
</span><span class="cx">     while (!$fetcher-&gt;at_end()) {
</span><span class="cx">         $content = $fetcher-&gt;fetch_next_cluster();
</span><span class="lines">@@ -164,9 +169,9 @@
</span><span class="cx"> 
</span><span class="cx">     function execute_query($config_id) {
</span><span class="cx">         return $this-&gt;db-&gt;query('
</span><del>-            SELECT test_runs.*, builds.*,
-            array_agg((commit_id, commit_repository, commit_revision, commit_time)) AS revisions,
-            max(commit_time) AS revision_time, max(commit_order) AS revision_order
</del><ins>+            SELECT test_runs.*, build_id, build_number, build_builder, build_time,
+            array_agg((commit_id, commit_repository, commit_revision, extract(epoch from commit_time) * 1000)) AS revisions,
+            extract(epoch from max(commit_time)) * 1000 AS revision_time, max(commit_order) AS revision_order
</ins><span class="cx">                 FROM builds
</span><span class="cx">                     LEFT OUTER JOIN build_commits ON commit_build = build_id
</span><span class="cx">                     LEFT OUTER JOIN commits ON build_commit = commit_id, test_runs
</span><span class="lines">@@ -181,7 +186,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     private static function format_run(&amp;$run, &amp;$commit_time) {
</span><del>-        $commit_time = Database::to_js_time($run['revision_time']);
</del><ins>+        $commit_time = intval($run['revision_time']);
</ins><span class="cx">         $build_time = Database::to_js_time($run['build_time']);
</span><span class="cx">         if (!$commit_time)
</span><span class="cx">             $commit_time = $build_time;
</span><span class="lines">@@ -211,7 +216,7 @@
</span><span class="cx">             $commit_id = intval(trim($name_and_revision[0], '&quot;'));
</span><span class="cx">             $repository_id = intval(trim($name_and_revision[1], '&quot;'));
</span><span class="cx">             $revision = trim($name_and_revision[2], '&quot;');
</span><del>-            $time = Database::to_js_time(trim($name_and_revision[3], '&quot;'));
</del><ins>+            $time = intval(trim($name_and_revision[3], '&quot;'));
</ins><span class="cx">             array_push($revisions, array($commit_id, $repository_id, $revision, $time));
</span><span class="cx">         }
</span><span class="cx">         return $revisions;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicincludemanifestphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/include/manifest.php (203485 => 203486)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/include/manifest.php        2016-07-21 02:09:51 UTC (rev 203485)
+++ trunk/Websites/perf.webkit.org/public/include/manifest.php        2016-07-21 03:25:59 UTC (rev 203486)
</span><span class="lines">@@ -106,7 +106,7 @@
</span><span class="cx">                 }
</span><span class="cx"> 
</span><span class="cx">                 array_push($current_platform_entry['metrics'], $metric_row['metric_id']);
</span><del>-                array_push($current_platform_entry['last_modified'], $metric_row['last_modified']);
</del><ins>+                array_push($current_platform_entry['last_modified'], intval($metric_row['last_modified']));
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">         $configurations = array();
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgservertestsapimeasurementsettestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js (203485 => 203486)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js        2016-07-21 02:09:51 UTC (rev 203485)
+++ trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js        2016-07-21 03:25:59 UTC (rev 203486)
</span><span class="lines">@@ -2,6 +2,9 @@
</span><span class="cx"> 
</span><span class="cx"> const assert = require('assert');
</span><span class="cx"> 
</span><ins>+require('../tools/js/v3-models.js');
+
+const MockData = require('./resources/mock-data.js');
</ins><span class="cx"> const TestServer = require('./resources/test-server.js');
</span><span class="cx"> const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
</span><span class="cx"> const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
</span><span class="lines">@@ -11,6 +14,10 @@
</span><span class="cx">     TestServer.inject();
</span><span class="cx">     connectToDatabaseInEveryTest();
</span><span class="cx"> 
</span><ins>+    beforeEach(function () {
+        MockData.resetV3Models();
+    });
+
</ins><span class="cx">     function queryPlatformAndMetric(platformName, metricName)
</span><span class="cx">     {
</span><span class="cx">         const db = TestServer.database();
</span><span class="lines">@@ -68,7 +75,7 @@
</span><span class="cx">         &quot;revisions&quot;: {
</span><span class="cx">             &quot;WebKit&quot;: {
</span><span class="cx">                 &quot;revision&quot;: &quot;144000&quot;,
</span><del>-                &quot;timestamp&quot;: clusterTime(10.3).toISOString(),
</del><ins>+                &quot;timestamp&quot;: clusterTime(10.35645364537).toISOString(),
</ins><span class="cx">             },
</span><span class="cx">         },
</span><span class="cx">         &quot;builderName&quot;: &quot;someBuilder&quot;,
</span><span class="lines">@@ -184,6 +191,38 @@
</span><span class="cx">         }).catch(done);
</span><span class="cx">     });
</span><span class="cx"> 
</span><ins>+    it(&quot;should be able to return an empty report&quot;, function (done) {
+        const db = TestServer.database();
+        Promise.all([
+            db.insert('tests', {id: 1, name: 'SomeTest'}),
+            db.insert('tests', {id: 2, name: 'SomeOtherTest'}),
+            db.insert('tests', {id: 3, name: 'ChildTest', parent: 1}),
+            db.insert('tests', {id: 4, name: 'GrandChild', parent: 3}),
+            db.insert('aggregators', {id: 200, name: 'Total'}),
+            db.insert('test_metrics', {id: 5, test: 1, name: 'Time'}),
+            db.insert('test_metrics', {id: 6, test: 2, name: 'Time', aggregator: 200}),
+            db.insert('test_metrics', {id: 7, test: 2, name: 'Malloc', aggregator: 200}),
+            db.insert('test_metrics', {id: 8, test: 3, name: 'Time'}),
+            db.insert('test_metrics', {id: 9, test: 4, name: 'Time'}),
+            db.insert('platforms', {id: 23, name: 'iOS 9 iPhone 5s'}),
+            db.insert('platforms', {id: 46, name: 'Trunk Mavericks'}),
+            db.insert('test_configurations', {id: 101, metric: 5, platform: 46, type: 'current'}),
+            db.insert('test_configurations', {id: 102, metric: 6, platform: 46, type: 'current'}),
+            db.insert('test_configurations', {id: 103, metric: 7, platform: 46, type: 'current'}),
+            db.insert('test_configurations', {id: 104, metric: 8, platform: 46, type: 'current'}),
+            db.insert('test_configurations', {id: 105, metric: 9, platform: 46, type: 'current'}),
+            db.insert('test_configurations', {id: 106, metric: 5, platform: 23, type: 'current'}),
+            db.insert('test_configurations', {id: 107, metric: 5, platform: 23, type: 'baseline'}),
+        ]).then(function () {
+            return TestServer.remoteAPI().getJSONWithStatus(`/api/measurement-set/?platform=46&amp;metric=5`).then(function (response) {
+                assert.equal(response.statusCode, 404);
+            }, function (error) {
+                assert.equal(error, 404);
+                done();
+            });
+        }).catch(done);
+    });
+
</ins><span class="cx">     it(&quot;should be able to retrieve a reported value&quot;, function (done) {
</span><span class="cx">         addBuilderForReport(reportWithBuildTime[0]).then(function () {
</span><span class="cx">             return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
</span><span class="lines">@@ -379,7 +418,7 @@
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    it(&quot;should create cache results&quot;, function (done) {
</del><ins>+    it(&quot;should create cached results&quot;, function (done) {
</ins><span class="cx">         const remote = TestServer.remoteAPI();
</span><span class="cx">         let cachePrefix;
</span><span class="cx">         addBuilderForReport(reportWithBuildTime[0]).then(function () {
</span><span class="lines">@@ -409,4 +448,26 @@
</span><span class="cx">         }).catch(done);
</span><span class="cx">     });
</span><span class="cx"> 
</span><ins>+    it(&quot;should use lastModified timestamp identical to that in the manifest file&quot;, function (done) {
+        const remote = TestServer.remoteAPI();
+        addBuilderForReport(reportWithBuildTime[0]).then(function () {
+            return remote.postJSON('/api/report/', reportWithRevision);
+        }).then(function () {
+            return queryPlatformAndMetric('Mountain Lion', 'Time');
+        }).then(function (result) {
+            return remote.getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&amp;metric=${result.metricId}`);
+        }).then(function (primaryCluster) {
+            return remote.getJSONWithStatus('/api/manifest').then(function (content) {
+                const manifest = Manifest._didFetchManifest(content);
+
+                const platform = Platform.findByName('Mountain Lion');
+                assert.equal(Metric.all().length, 1);
+                const metric = Metric.all()[0];
+                assert.equal(platform.lastModified(metric), primaryCluster['lastModified']);
+
+                done();
+            });
+        }).catch(done);
+    });
+
</ins><span class="cx"> });
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgtoolsjsremotejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/tools/js/remote.js (203485 => 203486)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/tools/js/remote.js        2016-07-21 02:09:51 UTC (rev 203485)
+++ trunk/Websites/perf.webkit.org/tools/js/remote.js        2016-07-21 03:25:59 UTC (rev 203486)
</span><span class="lines">@@ -119,6 +119,11 @@
</span><span class="cx">                 response.setEncoding('utf8');
</span><span class="cx">                 response.on('data', function (chunk) { responseText += chunk; });
</span><span class="cx">                 response.on('end', function () {
</span><ins>+                    if (response.statusCode != 200) {
+                        reject(response.statusCode);
+                        return;
+                    }
+
</ins><span class="cx">                     if ('set-cookie' in response.headers) {
</span><span class="cx">                         for (const cookie of response.headers['set-cookie']) {
</span><span class="cx">                             var nameValue = cookie.split('=')
</span></span></pre>
</div>
</div>

</body>
</html>