Zobrazit příspěvky

Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.


Příspěvky - exkalibr

Stran: 1 ... 18 19 [20] 21 22 ... 25
286
Na blogu jsem updatoval ten poslední obrázek. Ten připojený je vadný (v tom připojeném stejně je prd vidět).

Z odchylek jsem si uvědomil jednu věc, která přesně reflektuje skutečná čísla. Jelikož ten poslední graf čtu zprava doleva, na rozhraní každého nového bloku je vidět velký nárůst odchylky. Vysvětlení je v tom, že nový soubor trvá déle načíst a zapsat. Člověk by očekával mezi jednotlivými testy podobné odchylky, jenže T2 je trochu jiné - vypadá to, jako by se skoková odchylka oproti T6/T7 opozdila o několik cyklů. Na druhou stranu T7 má odchylku ještě před 4495 a 6758, člověk by očekával, že větší odchylka bude na začátku bloku, tak jako u těch menších bloků...

287
Odchylky od průměru ještě z jiné strany.

Větší soubory v původní velikosti si můžete zobrazit či stáhnout na mém blogu.

https://it-pomocnik.blogspot.com/2019/10/php-results-of-test-atomicity.html

288
Taky jsem počítal odchylky od průměru v testech T2 až T7.


289
Pak ale nechápu k čemu jsou ty křivky T2-T8b v grafu když u nich dochází k selhání. K čemu je důležité znát rychlost v sekundách když při tom dochází k selhání?

Protože vidíš, jak by trvala operace, kdyby tam nebyl ten @mkdir s prodlevou na uvolnění zámku. Uděláš si představu, jak rychlý je ten algoritmus i když ho pro toto řešení nevyužiješ.

Z předchozích výsledků například vyplynulo, že když zapisuješ přes file_put_contents do toho samého souboru, ze kterého pak zase čteš a furt dokola, je to rychlejší než když to zapisuješ do jiného souboru.

290
PRODLEVY

T2-T6 mají 50 mikrosekundové prodlevy, které nemají vliv na výsledný čas, ale mají vliv na to jestli dochází k selháním. Protože čím větší je prodleva smyčky, tím větší je pravděpodobnost kolize.

T7 nemá žádnou prodlevu

T8  nemá žádnou prodlevu přímo v hlavním bloku pro čtení a zápis, ale je tam ta prodleva, která se vytváří čekáním na uvolnění zámku vytvořeného přes @mkdir. Tato prodleva je jasně vidět na grafu, protože prodlužuje čas zpracování asi o 1/3 oproti T2 (žlutá čára) a o max. 25-50ms oproti T7.

T8a - (tmavězelenomodrá čára s černými čtverci) je bez korekce
T8b - (bílá čára) po odečtení toho 50 mikrosekundového zpoždění po každém zápisu.

T10 - tam je taky directory lock jako u T8, ale ještě před hlavním blokem (r/w), takže tento hlavní blok se vykoná úplně vždy.

T3 a T4 - bufferování file_get_contents() a file_put_contents()

Ty testy jsem začal dělat proto, že jsem se chtěl bufferování vyhnout. Chtěl jsem najít způsob jak přečíst a zapsat data bezpečně do souboru, aniž bych se musel bát, že když je pozměním, změny nebudou uloženy. Proto mě ty funkce file_get(put)_contents nezajímají, jsou pro mě nepoužítelné.

291
@Exkalibr: Pokud k tomu grafu nemáš žádné vyhodnocení, tak proč si nám ho sem dal? K čemu nám je užitečný?
Proč by měl řešit, zda je nám ten graf k užitku? Zhodnotit si ho může každý, stejně jako ho každý může ignorovat dle své volby.
Ano, může si ho každý z nás zhodnotit tak, že bude 2 dny zkoumat zdrojáky a zjišťovat co v tom grafu vlastně je. Proč by tím měl trávit čas každý z nás když to může pár větami shrnout Exkalibr, který tomu ty 2 dny už věnoval.

Jenže bez přečtení diskuse se prostě neobejdeš. Jak jinak se dozvíš co je T8? Proč bych to měl opakovat? Ty si přece u toho byl a sám si navrhl T8 @mkdir jako tu pojistku proti přepisům, vlastně si mi taky poradil jak odstranit tu chybu, že kontrola filesize je zbytečná a nespolehlivá. Mě pak došlo, že nemohu rozdělovat proces čtení a proces zápisu na dva bloky, ale musí se vše udělat v jednom.

Hlavní úspěch
je především v tom, že se mi povedlo vyloučit ty selhání zápisu a selhání atomicity, takže T10 je první test s validními výsledky.

Graf pak slouží hlavně pro orientaci v tom, že vidíš jaké jsou zdržení při zápisu na disk když běží 4 procesy ve smyčce bez prodlevy ... a potom jak se ten čas mění, když tam uměle strčíš prodlevu kvůli čekání na uvolnění directory lock.

292
@Exkalibr: Pokud k tomu grafu nemáš žádné vyhodnocení, tak proč si nám ho sem dal? K čemu nám je užitečný?

Domnívám se, že ten graf mluví za vše, já jsem ho ale okomentoval slovy:

Citace
Jak vidíte, zkopírování 2MB souboru + čtení a zápis v originále už začíná být znát. Nejvyšší čas (v poměru k velikosti souboru) je 2.7 MB.

Zajimavé pro mě je do kdy je užitečné pracovat s jak velkými soubory. S větším souborem nad 2MB se to už přestává vyplácet časově a hypoteticky by se mohlo zvýšit riziko kolize. Zajimavé je, že i když jsem soubor navíc ještě kopíroval u souborů menších než 2MB se to vleze a vyrovná se to křivce T8. T8 byl ten test kde se uplatnil mkdir na hlavní proces (čtení/zápis). Takže výsledky pod 2MB považuji za skvělé. Přece si pamatujete, že T8 hlásilo chyby atomicity.

T10 je řešením pro mě, protože i když kopírování zabírá čas na víc, povedlo se vyloučit chybu atomicity v hlavním bloku (w/r).

Toto řešení alespoň pro mě představuje větší spolehlivost než @mkdir v hlavním bloku.

293
Pokud to tu čte někdo kdo má práva k editaci prvního příspěvku, bylo by možné tam editací připojit ten poslední graf a odkaz na příspěvek #140 se závěrečným funkčním kódem?

Návštěvníkům, kteří zde zabloudí se asi nebude chtít vše pročítat, tak mohl bys k tomu grafu stručně napsat jaké z něj plyne tvé doporučení nebo závěr pro ostatní programátory PHP? Podle názvu této diskuze bude návštěvníky hlavně zajímat, kterou funkci (zámek) použít pro čtení/zápis souborů.

Závěr jsem chtěl napsat, ale pak jsem ho na poslední chvíli smazal. Lze použít script co jsem tu vložil - test10 - si upravit. Každý si ale musí doladit jaký chce použít mód. Já teď například píšu univerzální funkci pro zápis, která bude přidávat data. V praxi přece nikdo nechce jen načíst data. Musíš to udělat tak, že načteš data, pak provedeš operace a pak je uložíš. Což znamená znovu zkontrolovat velikost souboru, porovnat jestli velikost sedí nebo případně načíst celý soubor v modu r+, porovnat data, dát rewind a zapsat. Jen tak si člověk může ověřit že mezi zpracováváním dat nedošlo ke změně. Člověk se může rozhodnout jestli tu kontrolu dat chce udělat. Prostě záleží to na tom jak důležitý je ten souboru a jak velký provoz je na stránce. Kdo to porovnávat nechce, tak to dá do modu w (write) nebo a (append).

294
Teď to čtu v manuálu, že append mode pro fopen je atomický.

"If handle was fopen()ed in append mode, fwrite()s are atomic (unless the size of string exceeds the filesystem's block size, on some platforms, and as long as the file is on a local filesystem). That is, there is no need to flock() a resource before calling fwrite(); all of the data will be written without interruption."

295
A teď už konečně výsledky z posledního testu. A doufám, že tento byl už opravdu poslední.

Jak vidíte, zkopírování 2MB souboru + čtení a zápis v originále už začíná být znát. Nejvyšší čas (v poměru k velikosti souboru) je 2.7 MB.

První časy pro jednotlivé bloky a procesy:
Kód: [Vybrat]
523kB	0,848779202	0,257663012	0,257443905	0,001293182
948kB 0,001987934 1,540060997 0,002835035 1,537259817
1371kB 0,004508018 0,818147898 0,005446196 2,225590944
1913kB 1,965152025 3,108391047 0,008336067 3,122910023
2701kB 0,679250002 4,378324986 0,013814211 0,009026051
4495kB 3,45074296 3,259652853 0,037675858 0,021569967
6758kB 0,275954008 8,090615034 0,042881966 6,911487103

Chybovost považuju za 0, i když je fakt, že na začátku jsou prodlevy kvůli čekání na zámek pro copy().

296
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.

Kód: [Vybrat]
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
Kód: [Vybrat]
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:
Kód: [Vybrat]
    $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.

297
přidávání dat do souboru pomocí fopen(filename, "a") je taky neatomicitní?

Zkus raději podle manuálu
Kód: [Vybrat]
file_put_contents($filename, $data, FILE_APPEND | LOCK_EX);

To mohu použít jen v případě, že ve scriptu nejedou dva procesy paralelně, já myslel právě pro to použití dvou skriptů. Dám příklad:

Co chci zapsat do logu:
"Byl jsem tady Skript 2 čas 1:00:11.000"
"Byl jsem tady Skript 1 čas 1:00:11.002"
"Byl jsem tady Skript 3 čas 1:00:11.001"

Jestli je zapíše za sebou - jedno v jakém pořadí,
nebo se může stát, že vznikne kolize

např

Kód: [Vybrat]
Byl jsem tady Skript 2 čas 1:00:11.00
Byl jsem tady Skript 1 čas 21:00:11.002
Byl jsem tady Skript 3 čas 1:00:11.001

(Ubral nulu na konci prvního řádku)

298
přidávání dat do souboru pomocí fopen(filename, "a") je taky neatomicitní?

299
Tak asi to zkusím.

300
Na endoru jsem se kdysi díval a byla omezená trafikem. Pokud se nepletu. Už jsem se před časem díval ale to bylo mnoho lez zpátky a narážel jsem na samé limity. Například limit odeslaných požadavků na server databáze.

Stran: 1 ... 18 19 [20] 21 22 ... 25