Dokončil jsem skript a prakticky mi to už nehlásí chyby.
Tento test je blízký testu 7 - vycházel jsem z jeho modifikovaných verzí po opravách, které jste mi doporučili.
Hlavním problémem byl jak už tu psal Lin, zjišťování velikosti souboru. To nemohlo správně fungovat. Když jsem vynechal test velikosti souboru, tak vše jede. Původně jsem ten test přidal proto, že to celé nejelo: velikost souboru se měnila až k nule.
V tomto testu jsem přidal funkci na test existence zámku pro zálohu souboru.
Zámek na atomicitu čtení a zápisu jsem nepřidával. Zřejmě už samotný fakt, že probíhá kopírování, to celé časově nějak rozhodí a nedojde k té kolizi jako před tím. Tím kopírováním je to realističtější, jenže já to paradoxně stejně nemohu obnovit, protože v té části, kde bych to obnovoval není nastaven atomicitní zámek. A kdybych ho tam dal, tak zase může dojít k chybě atomicity fail.
Jediná chyba, která tak občas nastane je "atomicity fail" v prvním zámku, který spravuje zálohování souboru. Ten mě ale fakticky nezajímá.
Přikládám celý kód, protože už mi to fakt moc nemyslí a třeba v něm najdete nějaké věci, které stojí za zmínku.
clearstatcache();
$_DEBUG_ = false;
/*
folder.t1 - directory lock for backup copy
folder.t2 - directory lock for restore copy
*/
echo "Lock and flush tester.".time()."<br>";
$time_constant = 1571149526;
//die; // Remove this line when you set time_constant
while ( time()<$time_constant )
{
usleep(500);
}
/*
Creates directory lock for atomicity or waits
$dirPrefix = "lock"; // dir. lock name
$lockN = 1; // first lock
$lockTimes = 7; test 7 times
*/
function AtomicFuseTestTimes($dirPrefix, $n, $lockN, $lockTimes, $printError = true){
$start = atomicFuse($dirPrefix, $n,$lockN,0);
for ($i=1; $i <= $lockTimes; $i++ )
if (!$start)
$start = atomicFuse($dirPrefix, $n,$lockN,$i);
else
break;
if (!$start) $start = atomicFuse($dirPrefix, $n, $lockN, null, false);
if ( $printError==true && !$start) echo "<br><b>Atomicity failed for lock $lockN.</b><br> ";
return $start;
}
/*
Before or after, you call AtomicityTestLockTimes,
you need to create a directory lock.
$n = file size in kb
$lockN = 1; // first lock
$lockTimes = 7; test 7 times
*/
function AtomicityTestLockTimes($dirPrefix, $n, $lockN, $lockTimes, $printError = true){
$start = TestAtomicLock($dirPrefix, $n,$lockN,0);
for ($i=1; $i <= $lockTimes; $i++ )
if (!$start)
$start = TestAtomicLock($dirPrefix, $n, $lockN,$i);
else
break;
if (!$start) $start = TestAtomicLock($dirPrefix, $n, $lockN, null);
if ( $printError==true && !$start) echo "<br><b>Atomicity failed for lock $lockN.</b><br> ";
return $start;
}
/*
This makes a test of atomic lock but does not
create it.
c is counter for optimalization
first call must have c = 0;
*/
function TestAtomicLock($dirPrefix, $n, $lockNumber, $c){
if ( $c<0 )
die("<h3>TestAtomicLock: Error - c is less than 0</h3>");
$start = true;
echo "<br>'$dirPrefix.t$lockNumber'<br>";
if ( file_exists("$dirPrefix.t$lockNumber") )
{
clearstatcache();
$st = @stat("$dirPrefix.t$lockNumber");
echo "dir lock time: ".(time()-$st['ctime']);
if ( time()-$st['ctime'] > 10 )
{
@rmdir("$dirPrefix.t$lockNumber"); // remove directory lock
echo "<h1>Old lock removed</h1>";
}
$start = false;
}
else echo " not found; ";
if ( isset($c) ){
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)<br>";
}
}
return $start;
}
/*
dirPrefix - should be equal to name of the file you're trying to lock
lockNumber for adding more locks */
function atomicFuse($dirPrefix, $n, $lockNumber, $c){
if ( $c<0 )
die("<h3>TestAtomicLock: Error - c is less than 0</h3>");
$start = false;
if ( @mkdir("$dirPrefix.t$lockNumber") )
$start = true;
if ( isset($c) ){
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)<br>";
}
}
return $start;
}
function test($n, $p, $_DEBUG_){
// $delay_multiplier = $n*2.5;
$original_fname = "$n";
$cname = "$n.b"; // copy to restore
$working_filename = "$n.txt"; // source & target
echo "<h4>$n at ".time()."</h4>";
for ($i = 0; $i<50; $i++ ){
$start_time = microtime(true);
clearstatcache(); // needed for filesize and touch
//////////////////////
// TEST ATOMIC LOCK //
//////////////////////
// Test 1 - "backup" lock
// MAKE BACKUP
// Test 2 - "restoring" lock
$start = AtomicityTestLockTimes($n,$n,1,7);
if ( $start )
{
$success = @mkdir("$n.t1");
if ( $success == false )
echo "<b>Failed to lock;</b><br>";
$result = copy($original_fname, $cname);
if ( $result == false )
echo "<h4>BACKUP failed.</h4>";
$result = @rmdir("$n.t1");
if ( $result == false )
echo "<h4>Directory lock was not removed</h4>";
}
// Test 2 - "restoring" lock
// $start = AtomicityTestLockTimes($n,$n,2,7);
if ( true )
{
$st = stat("$working_filename");
$original_size = $st['size'];
if ( $_DEBUG_ )
echo "; 1) prevAccess by ".$st['mtime']." fsize ".$st['size']."; ";
$fsize = filesize($working_filename);
if ( $original_size <> $fsize )
die("; fsize total FAILTURE; ");
if ($fsize === 0)
die ("! <b>The fsize is 0</b>: fstat(): $original_size <>".$st['size']." ;");
// READ OPERATION AND LOCK FOR SHARE
$fp = fopen($working_filename, "r+");
$locked = flock($fp, LOCK_SH);
if ( $locked == false)
die("failed to get LOCK_SH;<br>");
$s = fread( $fp, $fsize );
// CHANGE LOCK
$success = flock($fp, LOCK_UN);
$locked = flock($fp, LOCK_EX);
if ( $success === false )
die("; r flock release failed; ");
if ( $locked == false )
die("failed to get LOCK_EX;<br>");
clearstatcache();
$st = stat("$working_filename");
if ( $_DEBUG_ )
echo "; 2) prevAccess by ".$st['mtime']." fsize is ".$fsize."; ";
// WRITE OPERATION WITH LOCK_EX
$locked = flock($fp, LOCK_EX);
if ( $locked ) { // acquire an exclusive lock
$success = rewind($fp);
if ($success == false)
echo " rewind failture; ";
$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; ";
// Test 2 - "restoring" lock
// $start = AtomicityTestLockTimes($n,$n,2,7);
$success = flock($fp, LOCK_UN);
$success = fclose($fp);
if (false)//$start)
{
$original_fname = "$n";
// Create directory lock
$success = @mkdir("$n.t2");
clearstatcache(); // Check file size
$st = fstat($fp);
if ($original_size>$st['size'])
{
echo "; <b>WRITE FAILED, restoring</b>;";
if ($success) {
$success = flock($fp, LOCK_UN);
$success = fclose($fp);
$result = copy($cname, $working_filename);
if ( $result == false )
echo "<h4>Restoring failed.</h4>";
}
if ($result == false )
die(" <b>TOTAL FAILTURE: copy failed.</b>");
else
echo " <b>RESTORED</b>;";
}
else
{ // FINISH SUCCESSFUL WRITE OPERATION
if ( $success )
touch("$working_filename",$fsize,$p);
$success = flock($fp, LOCK_UN); // release the lock
if ( $success === false )
echo "; release FAILED; ";
$success = fclose($fp);
if ( $success === false )
echo "; fclose FAILED; ";
// 10 - data načtená , $p - prohlížeč
if ( $success )
{
$result = touch("$working_filename",strlen($s),$p);
if ( $_DEBUG_ )
echo "; TOUCH: $result;";
}
else
die("fclose FAIL.");
}
// RELEASE DIRECTORY LOCK: (Test 2)
/*
$result = rmdir("$n.t2");
if ( $result == false )
echo "<h4>Directory lock was not removed</h4>";
*/
} // END OF ATOMIC LOCK (mkdir) Test 2
} else echo "Couldn't get the lock!";
}
/////////////////////////
// END OF TIME MEASURE //
/////////////////////////
$time_elapsed_secs = microtime(true) - $start_time;
//usleep( $delay_multiplier + $n*rand(2,6) );
if ( $time_elapsed_secs === 0 )
echo " FAILED ";
echo "time: $time_elapsed_secs s<br>";
} // for
}
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_);
Původní blok 7-8 testů a prodlev jsem přesunul do funkce:
AtomicityTestLockTimes
Která vlastně dělá jen
function AtomicityTestLockTimes($dirPrefix, $n, $lockN, $lockTimes, $printError = true){
$start = TestAtomicLock($dirPrefix, $n,$lockN,0);
for ($i=1; $i <= $lockTimes; $i++ )
if (!$start)
$start = TestAtomicLock($dirPrefix, $n, $lockN,$i);
else
break;
...
return $start;
}
Aplikace prvního zámku:
$start = AtomicityTestLockTimes($n,$n,1,7);
if ( $start )
{
$success = @mkdir("$n.t1");
if ( $success == false )
echo "<b>Failed to lock;</b><br>";
$result = copy($original_fname, $cname);
if ( $result == false )
echo "<h4>BACKUP failed.</h4>";
$result = @rmdir("$n.t1");
if ( $result == false )
echo "<h4>Directory lock was not removed</h4>";
}
A druhý zámek jsem neaplikoval, jak jsem už napsal.