Title: [236454] trunk/Websites/perf.webkit.org
Revision
236454
Author
dewei_...@apple.com
Date
2018-09-24 22:12:13 -0700 (Mon, 24 Sep 2018)

Log Message

Apache can return a corrupt manifest file while ManifestGenerator::store is running
https://bugs.webkit.org/show_bug.cgi?id=189822

Reviewed by Ryosuke Niwa.

Updating a file on performance dashboard should be transactional between php and apache.
Otherwise, partial content may be served.

* public/api/measurement-set.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
* public/api/runs.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
* public/include/db.php: Make creating file transactionaly by taking advantage of
'move/rename' operation is atomic in OS.
Added logic to avoid overwriting file if all content except specified filed are identical.
* public/include/json-header.php: Added a helper function that added 'status' but does not
convert content to json.
* public/include/manifest-generator.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
* public/shared/statistics.js: Removed unnecessary logging.
(Statistics.new.sampleMeanAndVarianceFromMultipleSamples):
* server-tests/api-manifest-tests.js: Updated one unit test.

Modified Paths

Diff

Modified: trunk/Websites/perf.webkit.org/ChangeLog (236453 => 236454)


--- trunk/Websites/perf.webkit.org/ChangeLog	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/ChangeLog	2018-09-25 05:12:13 UTC (rev 236454)
@@ -1,3 +1,25 @@
+2018-09-21  Dewei Zhu  <dewei_...@apple.com>
+
+        Apache can return a corrupt manifest file while ManifestGenerator::store is running
+        https://bugs.webkit.org/show_bug.cgi?id=189822
+
+        Reviewed by Ryosuke Niwa.
+
+        Updating a file on performance dashboard should be transactional between php and apache.
+        Otherwise, partial content may be served.
+
+        * public/api/measurement-set.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
+        * public/api/runs.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
+        * public/include/db.php: Make creating file transactionaly by taking advantage of
+        'move/rename' operation is atomic in OS.
+        Added logic to avoid overwriting file if all content except specified filed are identical.
+        * public/include/json-header.php: Added a helper function that added 'status' but does not
+        convert content to json.
+        * public/include/manifest-generator.php: Adapted invocation of 'generate_json_date_with_elapsed_time_if_needed'.
+        * public/shared/statistics.js: Removed unnecessary logging.
+        (Statistics.new.sampleMeanAndVarianceFromMultipleSamples):
+        * server-tests/api-manifest-tests.js: Updated one unit test.
+
 2018-08-22  Dewei Zhu  <dewei_...@apple.com>
 
         Show t-test results based on individual measurements to analysis task page.

Modified: trunk/Websites/perf.webkit.org/public/api/measurement-set.php (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/api/measurement-set.php	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/api/measurement-set.php	2018-09-25 05:12:13 UTC (rev 236454)
@@ -37,6 +37,7 @@
     }
 
     $cluster_count = 0;
+    $elapsed_time = NULL;
     while (!$fetcher->at_end()) {
         $content = $fetcher->fetch_next_cluster();
         $cluster_count++;
@@ -43,12 +44,12 @@
         if ($fetcher->at_end()) {
             $cache_filename = "measurement-set-$platform_id-$metric_id.json";
             $content['clusterCount'] = $cluster_count;
-            $content['elapsedTime'] = (microtime(true) - $program_start_time) * 1000;
+            $elapsed_time = (microtime(true) - $program_start_time) * 1000;
         } else
             $cache_filename = "measurement-set-$platform_id-$metric_id-{$content['endTime']}.json";
 
-        $json = success_json($content);
-        generate_data_file($cache_filename, $json);
+        set_successful($content);
+        $json = generate_json_data_with_elapsed_time_if_needed($cache_filename, $content, $elapsed_time);
     }
 
     echo $json;

Modified: trunk/Websites/perf.webkit.org/public/api/runs.php (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/api/runs.php	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/api/runs.php	2018-09-25 05:12:13 UTC (rev 236454)
@@ -39,9 +39,13 @@
     foreach ($config_rows as $config)
         $generator->fetch_runs($config['config_type'], $config['config_id'], $config['config_runs_last_modified']);
 
-    $content = success_json($generator->results());
+    $results = set_successful($generator->results());
     if (!$test_group_id)
-        generate_data_file("$platform_id-$metric_id.json", $content);
+        $content = generate_json_data_with_elapsed_time_if_needed("$platform_id-$metric_id.json", $results,  $generator->elapsed_time);
+    else {
+        $results['elapsedTime'] = $generator->elapsed_time;
+        $content = json_encode($results);
+    }
     echo $content;
 }
 
@@ -51,13 +55,15 @@
         $this->results = array();
         $this->last_modified = 0;
         $this->start_time = microtime(true);
+        $this->elapsed_time = NULL;
     }
 
     function results() {
+        $this->elapsed_time = (microtime(true) - $this->start_time) * 1000;
         return array(
             'configurations' => &$this->results,
-            'lastModified' => $this->last_modified,
-            'elapsedTime' => (microtime(true) - $this->start_time) * 1000);
+            'lastModified' => $this->last_modified
+        );
     }
 
     function fetch_runs($name, $config_id, $last_modified) {

Modified: trunk/Websites/perf.webkit.org/public/include/db.php (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/include/db.php	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/include/db.php	2018-09-25 05:12:13 UTC (rev 236454)
@@ -44,12 +44,35 @@
     return CONFIG_DIR . '/' . config($key) . '/' . $path;
 }
 
-function generate_data_file($filename, $content) {
-    if (!assert(ctype_alnum(str_replace(array('-', '_', '.'), '', $filename))))
+function same_till_last_occurrence_of_string($main_string, $comparing_string, $compare_to_last_position_string) {
+    if (!$compare_to_last_position_string)
+        return !strcmp($main_string, $comparing_string);
+
+    $position_in_main_string = strrpos($main_string, $compare_to_last_position_string);
+    $position_in_comparing_string = strrpos($comparing_string, $compare_to_last_position_string);
+    if ($position_in_main_string !== $position_in_comparing_string)
         return FALSE;
-    return file_put_contents(config_path('dataDirectory', $filename), $content, LOCK_EX);
+    if ($position_in_main_string === FALSE)
+        return !strcmp($main_string, $comparing_string);
+    return !substr_compare($main_string, $comparing_string, 0, $position_in_main_string);
 }
 
+function generate_json_data_with_elapsed_time_if_needed($filename, $object, $elapsed_time) {
+    if (!assert(ctype_alnum(str_replace(array('-', '_', '.'), '', $filename))))
+        return NULL;
+
+    $target_file_path = config_path('dataDirectory', $filename);
+    $object['elapsedTime'] = $elapsed_time;
+    $content = json_encode($object);
+
+    if (file_exists($target_file_path) && same_till_last_occurrence_of_string(file_get_contents($target_file_path), $content, 'elapsedTime'))
+        return $content;
+
+    $temp_file_path = tempnam(config_path('dataDirectory', ''), $filename . '-temp-');
+    file_put_contents($temp_file_path, $content, LOCK_EX);
+    return rename($temp_file_path, $target_file_path) ?  $content : NULL;
+}
+
 if (config('debug')) {
     error_reporting(E_ALL | E_STRICT);
     ini_set('display_errors', 'On');

Modified: trunk/Websites/perf.webkit.org/public/include/json-header.php (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/include/json-header.php	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/include/json-header.php	2018-09-25 05:12:13 UTC (rev 236454)
@@ -14,10 +14,14 @@
     exit(1);
 }
 
-function success_json($details = array()) {
+function set_successful(&$details = array()) {
     $details['status'] = 'OK';
     merge_additional_details($details);
+    return $details;
+}
 
+function success_json($details = array()) {
+    set_successful($details);
     return json_encode($details);
 }
 

Modified: trunk/Websites/perf.webkit.org/public/include/manifest-generator.php (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/include/manifest-generator.php	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/include/manifest-generator.php	2018-09-25 05:12:13 UTC (rev 236454)
@@ -9,6 +9,7 @@
 
     function __construct($db) {
         $this->db = $db;
+        $this->elapsed_time = NULL;
     }
 
     function generate() {
@@ -47,7 +48,7 @@
             'testAgeToleranceInHours' => config('testAgeToleranceInHours'),
         );
 
-        $this->manifest['elapsedTime'] = (microtime(true) - $start_time) * 1000;
+        $this->elapsed_time = (microtime(true) - $start_time) * 1000;
 
         return TRUE;
     }
@@ -55,7 +56,7 @@
     function manifest() { return $this->manifest; }
 
     function store() {
-        return generate_data_file('manifest.json', json_encode($this->manifest));
+        return generate_json_data_with_elapsed_time_if_needed('manifest.json', $this->manifest, $this->elapsed_time);
     }
 
     private function tests() {

Modified: trunk/Websites/perf.webkit.org/public/shared/statistics.js (236453 => 236454)


--- trunk/Websites/perf.webkit.org/public/shared/statistics.js	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/public/shared/statistics.js	2018-09-25 05:12:13 UTC (rev 236454)
@@ -72,7 +72,6 @@
         let sum = 0;
         let squareSum = 0;
         let size = 0;
-        console.log(samples);
         for (const sample of samples) {
             sum += sample.sum;
             squareSum += sample.squareSum;

Modified: trunk/Websites/perf.webkit.org/server-tests/api-manifest-tests.js (236453 => 236454)


--- trunk/Websites/perf.webkit.org/server-tests/api-manifest-tests.js	2018-09-25 04:21:32 UTC (rev 236453)
+++ trunk/Websites/perf.webkit.org/server-tests/api-manifest-tests.js	2018-09-25 05:12:13 UTC (rev 236454)
@@ -14,11 +14,8 @@
     it("should generate an empty manifest when database is empty", () => {
         return TestServer.remoteAPI().getJSON('/api/manifest').then((manifest) => {
             assert.deepEqual(Object.keys(manifest).sort(), ['all', 'bugTrackers', 'builders', 'dashboard', 'dashboards',
-                'elapsedTime', 'fileUploadSizeLimit', 'metrics', 'repositories', 'siteTitle', 'status', 'summaryPages', 'testAgeToleranceInHours', 'tests', 'triggerables']);
+                'fileUploadSizeLimit', 'metrics', 'repositories', 'siteTitle', 'status', 'summaryPages', 'testAgeToleranceInHours', 'tests', 'triggerables']);
 
-            assert.equal(typeof(manifest.elapsedTime), 'number');
-            delete manifest.elapsedTime;
-
             assert.deepStrictEqual(manifest, {
                 siteTitle: TestServer.testConfig().siteTitle,
                 all: {},
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to