PHP flock() alternative

后端 未结 7 1797
一个人的身影
一个人的身影 2020-12-09 11:40

PHP\'s documentation page for flock() indicates that it\'s not safe to use under IIS. If I can\'t rely on flock under all circumstances, is there another way I

7条回答
  •  北荒
    北荒 (楼主)
    2020-12-09 11:53

    None of these methods are fully atomic.

    I have made some tests, confirming this.

    The code for T7, using 7 files named by their size in kB:

    clearstatcache();
    $_DEBUG_ = false;
    
    echo "Lock and flush tester.".time()."
    "; $time_constant = 1570787996; die; // Remove this line when you set time_constant while ( time()<$time_constant ) { usleep(500); } function test($n, $p, $_DEBUG_){ // $delay_multiplier = $n*2.5; $sname = "$n"; // source $tname = "$n.txt";// target echo "

    $n at ".time()."

    "; for ($i = 0; $i<50; $i++ ){ $start = microtime(true); clearstatcache(); // needed for filesize and touch $st = stat("$sname"); $original_size = $st['size']; if ( $_DEBUG_ ) echo "; 1) prevAccess by ".$st['mtime']." fsize ".$st['size']."; "; $fsize = filesize($sname); if ( $original_size <> $fsize ) die("; fsize total FAILTURE; "); if ($fsize === 0) echo "! The fsize is 0: stat(): ".$st['size']." ;"; else { // READ OPERATION AND LOCK FOR SHARE $locked = false; for ($c = 0; !$locked; $c++): if ( $c > 400) break; $fp = fopen($sname, "r"); $locked = flock($fp, LOCK_SH); if ($locked) break; else { echo "failed to get LOCK_SH;
    "; usleep(5000); } endfor; $s = fread($fp, $fsize ); $success = flock($fp, LOCK_UN); if ( $success === false ) die("; r flock release failed; "); $success = fclose($fp); if ( $success === false ) die("; fclose failed; "); // 10 - loaded data , $p - broser if ( $success ) { $result = touch("$sname",strlen($s),$p); if ( $_DEBUG_ ) echo "; TOUCH: $result;"; } else die("fclose FAIL."); if ( strlen($s)<60 ) echo "*$s LENGTH:".strlen($s)."
    "; } clearstatcache(); $st = stat("$tname"); if ( $_DEBUG_ ) echo "; 2) prevAccess by ".$st['mtime']." fsize is ".$fsize."; "; // WRITE OPERATION WITH LOC_EX $fp = fopen($tname, "w"); $locked = false; /* // TOTO NEMÁ VLIV NA ZAMKNUTÍ for ($c = 0; !$locked; $c++ ): $c++; if ( $c > 400) break; $locked = flock($fp, LOCK_EX); if ($locked) break; else { echo "failed to get LOCK_EX;
    "; usleep(5000); } endfor; */ $locked = flock($fp, LOCK_EX); if ( $locked ) { // acquire an exclusive lock $success = fwrite($fp, $s); if ( $success === false) echo "; w FAILED;"; else if ( $_DEBUG_ ) echo " $success B written; "; $success = fflush($fp);// flush output before releasing the lock if ( $success === false ) echo "; flush FAILED; "; $success = flock($fp, LOCK_UN); // release the lock if ( $success === false ) echo "; release FAILED; "; $success = fclose($fp); if ( $success === false ) echo "; fclose FAILED; "; clearstatcache(); // needed for filesize and touch $fsize = filesize($tname); if ($original_size>$fsize) { echo "; WRITE FAILED, restoring;"; $original_fname = "$n"; $result = copy($original_fname, $tname); if ($result == false ) die(" TOTAL FAILTURE: copy failed."); else echo " RESTORED;"; } else { if ($fsize === 0) echo "! THE FILE WAS NOT WRITTEN: data length: ".strlen($s)." fsize: $fsize RESOURCE: $fp
    "; if ( $success ) touch("$tname",$fsize,$p); } } else { echo "Couldn't get the lock!"; } $time_elapsed_secs = microtime(true) - $start; //usleep( $delay_multiplier + $n*rand(2,6) ); if ( $time_elapsed_secs === 0 ) echo " FAILED "; echo "time: $time_elapsed_secs s
    "; } } // headers to identify originator of the request switch ( $_SERVER['HTTP_USER_AGENT'] ): // FF 1: case "Mozilla/5.0 (Windows NT 5.1;) Gecko": $p = 1; break; // Chrome: case "Mozilla/5.0 (Windows NT 5.1) AppleWebKit Chrome Safari": $p = 2; break; // OPERA: case "Mozilla/5.0 (Windows NT 5.1) AppleWebKit Chrome Safari": $p = 3; break; endswitch; copy("523","523.txt"); copy("948","948.txt"); copy("1371","1371.txt"); copy("1913","1913.txt"); copy("2701","2701.txt"); copy("4495","4495.txt"); copy("6758","6758.txt"); test("523",$p,$_DEBUG_); test("948",$p,$_DEBUG_); test("1371",$p,$_DEBUG_); test("1913",$p,$_DEBUG_); test("2701",$p,$_DEBUG_); test("4495",$p,$_DEBUG_); test("6758",$p,$_DEBUG_);

    The code for T8 (mkdir lock test):

    clearstatcache();
    $_DEBUG_ = false;
    
    echo "Atomicity tester.".time()."
    "; $time_constant = 1570787996; die; // Remove this line when you set time_constant while ( time()<$time_constant ) { usleep(500); } /* c is counter for optimalization first call must have c = 0; */ function atomicFuse($n, $c, $disableDelay = false){ $start = false; if ( !file_exists("$n.t") ) $start = mkdir("$n.t"); if ( !$disableDelay ){ if ( $start == false ) { $n = $n*30; switch($c): // Delay example increase: case 0: break; // 0,01569 total case 1: break; // 0,03138 total case 2: $n = $n*2; break; // 0,06276 total case 3: $n = $n*4; break; // 0,12552 total // case 4: You need at least *6 or *8 to get out of problems with extrem times case 4: $n = $n*8; break; // 0,25104 t.(upper limit) // In case of heavy traffic: case 5: $n = $n*8; break; // 0,36087 total extrem case 6: $n = $n*10; break; // 0,51777 total extrem case 7: $n = $n*20; break; // 1,03554 total extrem default: $n = $n*8; break; endswitch; usleep($n); echo ($n)."
    "; } } return $start; } function test($n, $p, $_DEBUG_){ $fp = null; $sname = "$n"; // source $tname = "$n.txt";// target echo "

    $n at ".time()."

    "; for ($i = 0; $i<50; $i++ ){ $start_time = microtime(true); { $start = atomicFuse($n,0); if (!$start) $start = atomicFuse($n,1); if (!$start) $start = atomicFuse($n,2); if (!$start) $start = atomicFuse($n,3); if (!$start) $start = atomicFuse($n,4); if (!$start) $start = atomicFuse($n,5); if (!$start) $start = atomicFuse($n,6); if (!$start) $start = atomicFuse($n,7); if (!$start) $start = atomicFuse($n, false); if (!$start) echo "Atomicity failed. "; if ( $start ) { echo "Atomicity OK. "; ///////////////////////////// // CHECK FILESIZE VALIDITY // ///////////////////////////// clearstatcache(); // needed for filesize and touch $st = stat("$sname"); $original_size = $st['size']; if ( $_DEBUG_ ) echo "; 1) prevAccess by ".$st['mtime']." fsize ".$st['size']."; "; $fsize = filesize($sname); if ( $original_size <> $fsize ) die("; fsize total FAILTURE; "); if ($fsize === 0) echo "! The fsize is 0: stat(): ".$st['size']." ;"; /////////////////// // OPEN THE FILE // /////////////////// $fp = fopen($sname, "r"); $s = fread($fp, $fsize ); $success = fclose($fp); if ( $success === false ) die("; fclose failed; "); // 10 - loaded data, $p - browser if ( $success ) { $result = touch("$sname",strlen($s),$p); if ( $_DEBUG_ ) echo "; TOUCH: $result;"; } else die("fclose FAIL."); if ( strlen($s)<60 ) echo "*$s LENGTH:".strlen($s)."
    "; } } if ( $start ) { clearstatcache(); $st = stat("$tname"); if ( $_DEBUG_ ) echo "; 2) prevAccess by ".$st['mtime']." fsize is ".$fsize."; "; // WRITE OPERATION WITH LOC_EX $fp = fopen($tname, "w"); if ( true ) { // acquire an exclusive lock $success = fwrite($fp, $s); if ( $success === false) echo "; w FAILED;"; else if ( $_DEBUG_ ) echo " $success B written; "; $success = fflush($fp);// flush output before releasing the lock if ( $success === false ) echo "; flush FAILED; "; if ( $success === false ) echo "; release FAILED; "; $success = fclose($fp); if ( $success === false ) echo "; fclose FAILED; "; clearstatcache(); // needed for filesize and touch $fsize = filesize($tname); if ($original_size>$fsize) { echo "; WRITE FAILED, restoring;"; $original_fname = "$n"; $result = copy($original_fname, $tname); if ($result == false ) die(" TOTAL FAILTURE: copy failed."); else echo " RESTORED;"; } else { if ($fsize === 0) echo "! THE FILE WAS NOT WRITTEN: data length: ".strlen($s)." fsize: $fsize RESOURCE: $fp
    "; if ( $success ) touch("$tname",$fsize,$p); } } else { echo "Couldn't get the lock!"; } $success = rmdir("$n.t"); // remove atomic fuse if ( $success ) echo "

    DIR REMOVED

    "; else echo "

    DIR NOT REMOVED

    "; } // start else echo "skipped"; $time_elapsed_secs = microtime(true) - $start_time; if ( $time_elapsed_secs === 0 ) echo " FAILED "; echo "time: $time_elapsed_secs s
    "; } // for } switch ( $_SERVER['HTTP_USER_AGENT'] ): case "": $p = 1; break; case "": $p = 2; break; case "": $p = 3; break; endswitch; copy("523","523.txt"); copy("948","948.txt"); copy("1371","1371.txt"); copy("1913","1913.txt"); copy("2701","2701.txt"); copy("4495","4495.txt"); copy("6758","6758.txt"); test("523",$p,$_DEBUG_); test("948",$p,$_DEBUG_); test("1371",$p,$_DEBUG_); test("1913",$p,$_DEBUG_); test("2701",$p,$_DEBUG_); test("4495",$p,$_DEBUG_); test("6758",$p,$_DEBUG_);

    Note: T5-T7 - I did not determinated whether the file damages were made by fflush or fwrite, but it was in these tests, where these error occures.

    Note: T8 - Specific problem with this test is, that it often waits too long on begin of a testing block (on the begin of the testing function). There even delays like 7 seconds waiting. But I also tried to remove these numbers and the avarage does not change too much, so the curve of T8 would stay the same after this change. The problem here is that using the delay in a loop is not ideal solution of the problem, it makes the probability of failture even higher. Note, that by "failture" I do not really mean file corruption but skipping of the given atomic task because time out.

提交回复
热议问题