Commit:    b8f48f651c5e80d2a877316becc75a6a25ee6c6f
Author:    Anatol Belski <[email protected]>         Tue, 19 Sep 2017 12:11:09 +0200
Parents:   6659d824d1a42db91fcf8aaa117abae3f4dfc316
Branches:  master

Link:       
http://git.php.net/?p=web/rmtools.git;a=commitdiff;h=b8f48f651c5e80d2a877316becc75a6a25ee6c6f

Log:
Rewrite for atomic rw for the db file

Changed paths:
  M  include/Branch.php


Diff:
diff --git a/include/Branch.php b/include/Branch.php
index c26cc21..b87c38d 100644
--- a/include/Branch.php
+++ b/include/Branch.php
@@ -12,6 +12,7 @@ class Branch {
        public $config;
        private $repo;
        public $db_path;
+       protected $db_fd;
        public $data = NULL;
 
        public function __construct($config_path)
@@ -25,7 +26,7 @@ class Branch {
                $this->repo->setBranch($this->config->getRepoBranch());
                $this->db_path = __DIR__ . '/../data/db/' . 
$this->config->getName() . '.json';
 
-               $this->data = $this->readdata();
+               $this->data = $this->readData();
        }
 
        protected function getEmptyData()
@@ -40,22 +41,84 @@ class Branch {
                return $data;
        }
        
-       protected function readData()
+       /* If both read and write are requested, read and yield data first. The 
fd
+               is still open and the exclusive lock is held. Call the function 
once
+               more to write. */
+       protected function atomicDataRW($read = true, $write = false)
        {
-               $fd = fopen($this->db_path, "rb");
-               if ($fd) {
-                       flock($fd, LOCK_SH);
+               if ($write) {
+                       $open_mode = "cb+";
+                       $lock_mode = LOCK_EX;
+               } else {
+                       $open_mode = "rb";
+                       $lock_mode = LOCK_SH;
+               }
+
+               if (!$this->db_fd) {
+                       $this->db_fd = fopen($this->db_path, $open_mode);
+                       if (!$this->db_fd) {
+                               $this->db_fd = NULL;
+                               throw new \Exception("Failed to open 
{$this->db_path}.");
+                       }
+                       flock($this->db_fd, $lock_mode);
+               }
+
+               if ($read) {
                        $j = "";
-                       while(!feof($fd)) {
-                               $j .= fread($fd, 1024);
+                       while(!feof($this->db_fd)) {
+                               $j .= fread($this->db_fd, 1024);
                        }
                        $data = json_decode($j);
-                       flock($fd, LOCK_UN);
-                       fclose($fd);
-               } else {
+
+                       if (!$write) {
+                               flock($this->db_fd, LOCK_UN);
+                               fclose($this->db_fd);
+                               $this->db_fd = NULL;
+                       }
+                       return $data;
+               }
+
+               if ($write) {
+                       $json = json_encode($this->data, JSON_PRETTY_PRINT);
+                       $to_write = strlen($json);
+                       $wrote = 0;
+
+                       rewind($this->db_fd);
+                       do {
+                               $got = fwrite($this->db_fd, substr($json, 
$wrote));
+                               if (false == $got) {
+                                       break;
+                               }
+                               $wrote += $got;
+                       } while ($wrote < $to_write);
+
+                       if ($to_write !== $wrote) {
+                               flock($this->db_fd, LOCK_UN);
+                               fclose($this->db_fd);
+                               $this->db_fd = NULL;
+                               throw new Exception("Couldn't write 
'{$this->db_path}'");
+                       }
+
+                       flock($this->db_fd, LOCK_UN);
+                       fclose($this->db_fd);
+                       $this->db_fd = NULL;
+
+                       return $wrote > 0;
+               }
+       }
+
+       protected function readData()
+       {
+               try {
+                       $data = $this->atomicDataRW(true, false);
+               } catch (\Exception $e) {
                        $data = $this->getEmptyData();
+               } finally {
+                       if (!$data) {
+                               $data = $this->getEmptyData();
+                       }                       
                }
-               
+
                if ($data->build_num > self::REQUIRED_BUILDS_NUM) {
                        throw new \Exception("Inconsistent db, build number 
can't be {$data->build_num}.");
                }
@@ -65,9 +128,7 @@ class Branch {
 
        protected function writeData()
        {
-               /* This might be an issue under high concurrency. */
-               $json = json_encode($this->data, JSON_PRETTY_PRINT);
-               return file_put_contents($this->db_path, $json, LOCK_EX);
+               return $this->atomicDataRW(false, true);
        }
 
        private function addBuildList($build_name = NULL)
@@ -105,21 +166,16 @@ class Branch {
 
        public function update($build_name = NULL)
        {
-               $fd = fopen($this->db_path, "cb+");
-               if (!$fd) {
-                       throw new \Exception("Failed to open 
{$this->db_path}.");
-               }
-               flock($fd, LOCK_EX);
-               $j = "";
-               while(!feof($fd)) {
-                       $j .= fread($fd, 1024);
-               }
-               $got_data = strlen($j) > 0;
-               if ($got_data) {
-                       $this->data = json_decode($j);
-               } else {
-                       $this->data = $this->getEmptyData();
+               try {
+                       $data = $this->atomicDataRW(true, true);
+               } catch (\Exception $e) {
+                       $data = $this->getEmptyData();
+               } finally {
+                       if (!$data) {
+                               $data = $this->getEmptyData();
+                       }                       
                }
+               $this->data = $data;
                
                $last_id = $this->repo->getLastCommitId();
                
@@ -137,27 +193,9 @@ class Branch {
        
                $this->addBuildList($build_name);
 
-               $json = json_encode($this->data, JSON_PRETTY_PRINT);
-               $to_write = strlen($json);
-               $wrote = 0;
 
-               rewind($fd);
-               do {
-                       $got = fwrite($fd, substr($json, $wrote));
-                       if (false == $got) {
-                               break;
-                       }
-                       $wrote += $got;
-               } while ($wrote < $to_write);
+               return $this->atomicDataRW(false, true);
 
-               flock($fd, LOCK_UN);
-               fclose($fd);
-               
-               if ($to_write !== $wrote) {
-                       throw new Exception("Couldn't cache 
'{$this->db_path}'");
-               }
-               
-               return true;
        }
 
        public function hasUnfinishedBuild()


--
PHP Webmaster List Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to