Ankündigung

Einklappen
Keine Ankündigung bisher.

Streams, Abstraktion von Dateizugriff

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Streams, Abstraktion von Dateizugriff

    Hi.

    Ich versuche derzeit für eine Library zu evaluieren, wie mit Beziehungen zu externen Ressourcen (im Sinne von Dateien) umgegangen werden kann.

    Sagen wir mal, ich will eine Methode wie DOMDocument::load schreiben, die eine Datei einliest und in irgendeiner Form verarbeitet.

    Einige denkbare Signaturen:
    1. load(string $data)
    2. load(string $url)
    3. load(stream $stream)


    Variante 1: „Ich will mit dem Dateisystemzugriff nichts zu tun haben, gib mir einfach nur die Daten.“ Das ist ein schönes Level an Abstraktion, aber alle Daten müssen zeitgleich und vielleicht gar mehrfach im Speicher gehalten werden, was sehr ineffizient sein kann und in manchen Fällen überhaupt keine Option darstellt.

    Variante 2: „Gib mir einen URL und ich versuche selbst mein Glück und sage Bescheid, wenn es fehlschlägt.“ Hier habe ich bewusst $url geschrieben und nicht etwa $localPath, um anzudeuten, dass $url im Grunde alle fopen-Wrapper umfasst und auch alle per stream_wrapper_register zusätzlich definierten Wrapper.[1] Da stehen die Chancen relativ gut, dass die Methode den URL überhaupt nicht „geöffnet“ bekommt, weil die Ressource etwa gar nicht gelesen werden kann oder sonst irgendwas ist. Diese Fehler müssen dann irgendwie formuliert und an den aufrufenden Code weitergereicht werden. Zudem muss eine Entscheidung getroffen werden, ob und für welche Protokolle ein Lock gesetzt wird und was passiert, wenn das fehlschlägt, oder auch, auf welche Weise genau auf den Stream zugegriffen wird (fopen-Modi). Anders gesagt: Das ist reichlich unübersichtlich und genau das Gegenteil von Abstraktion.
    Variante 2b: Es werden nur solche Pfade zugelassen, die auf das lokale Dateisystem zeigen. Wer was anderes übergibt, hat selbst Schuld. Das ist vermutlich das, was die meisten Methoden, die Pfade als Parameter nutzen, implizieren. Durchsetzen werden es die wenigsten. Diese Variante hat allerdings so oder so noch immer mit einem Teil der Probleme von Variante 2 zu tun.

    Variante 3: „Gib mir einen Stream, der so eingestellt ist, wie du es willst. Ich funke da nicht zwischen, sondern sag dir höchstens an, falls es knallt.“ Im Vergleich zu Variante 2 verlangt diese Variante, dass der aufrufende Code alle notwendigen Entscheidungen trifft und bereits einen Stream bereitstellt, der dann ja ganz offensichtlich geöffnet werden konnte. Zudem ist theoretisch eine sehr flexible Einbindung möglich, denn auch Funktionen wie popen erzeugen gleichartige Streams. Auch lässt sich ein solcher Stream sicherlich hübsch als Klasse zusammenfassen, was garantiert auch bereits jemand getan hat.

    Variante 4: void load(stream $in, stream $out). Das ist für normale Anwendungsfälle wohl zu viel des Guten, auch wenn sich ein Out-Stream etwa per stream_get_contents wieder in einen String verwandeln ließe.


    Alle Varianten außer 2b decken den Umfang der jeweiligen Vorgängervariante vollständig ab.

    Ich tendiere zu Variante 3. Was meint ihr?



    PS: Mir ist schon klar, dass das alles nicht wirklich neu ist, aber es ist ja mehr oder weniger eine Tatsache, dass Variante 2 vorherrschend ist. Warum eigentlich? Weil wir nicht Java sind?



    1: Auch möglich: $url = 'data://text/plain;base64,' . base64_encode($data);

  • #2
    Wenn du so flexibel sein willst, würde ich auch auf den Stream gehen. Und dann eben die einzlnen Stream-Klassen, z.b. Memory-Stream zum Laden / Aufbereiten der entsprechenden Quellen.

    Kommentar


    • #3
      Ich würde zu Variante 4b) tendieren:

      void load(stream $in)
      void setOutputStream(stream $out) - zumindest wenn Deine Klasse überhaupt sowas wie Ausgaberoutinen hat.

      Zusätzlich zu load() spricht ja nichts dagegen, eine weitere Methode zur Verfügung zu stellen, die direkt einen String akzeptiert. Je nach konkreter Eingabe dürfte die Aufrufwahrscheinlichkeit 50:50 sein und Du sparst Deinem Anwender das Generieren eines Data-Streams.

      Die letztendliche Frage wird sich aber wahrscheinlich nur in Kenntnis des konkreten Kontextes beantworten lassen, denke ich.

      Gruß Jens

      Kommentar


      • #4
        Das ist wohl eine Frage die nur im Kontext beantwortet werden kann. Im Prinzip sind je nach Anwendungsfall alle 3 Varianten sinnvoll.

        Variante 1... wenn du die Daten schon im Speicher hast macht es wenig Sinn diese in einen Memory Stream zu kopieren. Erstens ist das "umständlich", zweitens kostet das dann wirklich doppelt Speicher. PHP arbeitet intern per CopyOnWrite, solange du den Inhalt der übergebenen Daten nicht änderst bleibt es nur eine Referenz auf das Orginal.

        Variante 2... der Einfachheit halber und siehe 3...

        PHP-Code:
        $fh fopen('file.dat''r');
        $foo->load($fh);
        fclose($fh); 
        PHP-Code:
        $foo->load('file.dat'); 
        Dazu würde auch noch die Fehlerbehandlung kommen. Einfach blind ein Filehandler zu übergeben ist ja auch unschön.

        Variante 3... bei umfangreichen Daten die nicht auf einmal in den Speicher sollen oder gar passen. Da muss der Rest natürlich drauf angepasst sein. Z.B. bei dem von dir geposteten DOMDocument ist das nicht der Fall...

        Kommentar


        • #5
          Danke für die Antworten.

          Noch einige konkretere Aussagen, warum ich Variante 2 so problematisch finde:

          Generell befürchte ich Race-Conditions, wenn sowohl schreibender als auch lesender PHP-Code existiert. Probleme mit externen Anwendungen, die auf die Dateien zugreifen, will ich dabei gar nicht mal groß beachten.

          Ich habe vor dem Erstellen dieses Threads lange über Locking-Mechanismen nachgeforscht, bin aber nicht wirklich zu einem guten Ergebnis gekommen.

          - http://stackoverflow.com/questions/3...-a-mess-in-php

          Dazu herrscht allgemein wohl wieder so eine typische PHP-Haltung: „Es funktioniert doch gut genug. Im Zweifel hackst du dir was zusammen (GET_LOCK in MySQL oder so). Ob das im Detail alles exakt ist, können wir nicht sagen. Wir kleben mit PHP ohnehin nur externe Libraries zusammen.“

          High-Level-Befehle wie file_get_contents sind nicht atomar, schützen also überhaupt nicht vor Race-Conditions. Ich kann mir auch nicht vorstellen, dass Library-Bindings wie DOMDocument::load locken.

          Klar, PHP ist vom Fokus und von der Ausrichtung der Dokumentation her nicht Boost. Aber es sollte Mittel und Wege geben, solche Low-Level-Angelegenheiten halbwegs seriös zu programmieren.

          Gerade bei Libraries ist das von erhöhter Bedeutung, da ich die auf eine Weise programmieren muss, die keine Hacks für konkrete Anwendungsszenarien enthält. Ich muss eine möglichst robuste und möglichst allgemeine Lösung finden.

          - http://stackoverflow.com/questions/2...xclusion-mutex

          Dort stehen in der Antwort von Anthony Ferrara (ircmaxell) einige Beispiele, wie mit flock zu arbeiten ist. Das entspricht im Wesentlichen der php.net-Dokumentation.

          Das Problem ist nur: Das verursacht Warnings und Notices ohne Ende (also Ausgaben), wenn auch nur die kleinste Sache nicht klappt. Da muss PHP entweder stumm konfiguriert werden oder es muss gehackt werden.

          - https://github.com/mermshaus/kaloa-x...eader.php#L196

          Hier am Beispiel von DOMDocument::load, bei fopen und Co. ist das aber genau dasselbe. Da PHP erst demnächst finally spendiert bekommt, wird entsprechender Code, der auch nur halbwegs korrekt arbeitet, unfassbar sperrig. (Ein anderes Experiment zum Thema weiter unten in Anhang 1.)

          Zusammenfassend gesagt: Jede Stelle im Code, die „Dateiinhalt lesen oder Exception“ sauber implementieren will, müsste sich redundant der angesprochenen Thematik stellen. Deshalb der Ansatz, ein Stream-Handle zu übergeben und den größten Teil der Entscheidungen in den aufrufenden Code zu verlagern. Das ist sozusagen der Dependency-Injection-Weg, der eigentlich meist eine ganz gute Sache ist.
          Am Rande: Um das Thema weiter zu verkomplizieren: Die Frage, ob das „alte“ PHP-Error-Modell oder Exceptions genutzt werden sollen, ist gar nicht so leicht zu beantworten. Wenn ich alles auf Exceptions umstellen will, bin ich schnell in einem Bereich, in dem ich mich gegen die Art und Weise richte, wie große Teile von PHP nun mal funktionieren. Das macht es in meinen Augen auch schwierig, PHP hier über Gebühr zu kritisieren. Die Warning/Notice-Funktionalität ist im Grunde einfach Sprachdesign. Muss man nicht mögen, aber sollte man akzeptieren.



          Konkret zu den Antworten:

          Zitat von mquadrat
          Wenn du so flexibel sein willst, würde ich auch auf den Stream gehen.
          Einerseits geht es um Flexibilität, aber andererseits – und wohl in erster Linie – weiß ich auch einfach nicht, wie ich es anders sauber hinbekommen soll. Stichwort Race-Conditions eben. Die müssen zwar in den meisten Fällen sicherlich nicht vermieden werden, weil kein Lese-Schreibzugriff vorgesehen ist. Aber wenn es möglich ist, sie zu vermeiden, warum denn nicht?

          PHP-Code:
          $h fopen('file.xml''rb');

          if (
          flock($hLOCK_SH)) {
              
          $dom = new DOMDocument();
              
          $dom->load('file.xml');
              
          flock($hLOCK_UN);
          }

          fclose($h); 
          *schulterzuck* Da kann ich der Instanz auch gleich das Handle geben.

          Zitat von Jens
          Variante 4b
          Ja, das ist natürlich auch eine Frage des Kontexts. Variante 4 hatte ich mehr so als Hinweis auf die theoretische Möglichkeit mit aufgenommen. Die Möglichkeit, einen Out-Stream zuzuweisen, ist wohl nur dann interessant, wenn es darum geht, Daten tatsächlich an einen externen Ort zu schieben, der nicht mehr zum Speicherbereich der laufenden PHP-Anwendung gehört (Datei, Pipe, …).

          Zusätzlich zu load() spricht ja nichts dagegen, eine weitere Methode zur Verfügung zu stellen, die direkt einen String akzeptiert. Je nach konkreter Eingabe dürfte die Aufrufwahrscheinlichkeit 50:50 sein und Du sparst Deinem Anwender das Generieren eines Data-Streams.
          Das wäre je nach Anwendung schon ein Gewinn hinsichtlich der Performance, wenn die Konvertierung wegfällt, stimmt. Siehe auch ercs Post. Das wären dann loadStream(stream) und loadString(string). Wobei ich sagen muss, dass ich vereinheitlichte Schnittstellen und eine möglichst geringe Anzahl Methoden mag und dass Streams eben beides abdecken. Zum Zugriff auf einen Stream, der „paketweise“ reinkommt, braucht es zudem möglicherweise anderen Code als beim Zugriff auf einen fertigen String. Aber ja, kontextabhängig.



          erc habe ich nicht mehr geschafft. Vielleicht nachher noch. Die meisten Dinge sind aber wohl gesagt.



          Anhang 1:

          PHP-Code:
          <?php

          error_reporting
          (-1);
          ini_set('display_errors'1);

          set_error_handler(function ($a$b){
              echo 
          'other custom handler';
          });



          // Use 'rb' for reading and 'cb' (!) for writing
          function file_read($path$mode$lockModeClosure $bodyFunc)
          {
              
          set_error_handler(function () { throw new Exception(); });

              
          $lock false;
              
          $fh false;

              try {
                  
          $fh fopen($path$mode);

                  if (
          $fh) {
                      if (
          flock($fh$lockMode)) {
                          
          $lock true;
                          
          $bodyFunc($fh);
                      } else {
                          throw new 
          Exception();
                      }
                  }
              } catch (
          Exception $e) {
                  
          // https://wiki.php.net/rfc/finally
                  
          if ($fh) {
                      if (
          $lock) {
                          
          flock($fhLOCK_UN);
                      }
                      
          fclose($fh);
                  }
                  
          restore_error_handler();
                  throw 
          $e;
              }

              
          // https://wiki.php.net/rfc/finally
              
          if ($fh) {
                  if (
          $lock) {
                      
          flock($fhLOCK_UN);
                  }
                  
          fclose($fh);
              }
              
          restore_error_handler();
          }

          $data '';

          file_read(__FILE__'rb'LOCK_SH, function ($handle) use (&$data) {
              while ((
          $buffer fgets($handle)) !== false) {
                  
          $data .= $buffer;
              }
          });

          var_dump($data);


          // Trigger "other custom handler"
          fopen(__DIR__ '/doesntexist''rb');

          Kommentar


          • #6
            Schreib einfach nicht in die Dateien von denen du liest.

            So in der Art:
            PHP-Code:
            function writeFile($filename$content) {
                
            $newFilename $filename ".new";
                
            $backupFilename $filename ".bak";

                
            file_put_contents($newFilename$content);
                
                if (
            file_exists($backupFilename)) {
                    
            unlink($backupFilename);
                }
                
            rename($filename$backupFilename);
                
            rename($newFilename$filename);

            Und parallele Schreibzugriffe solltet man mit Files in einer Webumgebung besser eh vermeiden und gleich eine Datenbank bemühen.

            Grüße.

            Kommentar


            • #7
              Das Problem ist, dass du meines Wissens absolut keine Garantie hast, dass nicht zwischen der Abarbeitung zweier Zeilen aus deiner Beispielfunktion die komplette Funktion von einem anderen Prozess ausgeführt wird. Das kann sogar passieren, während beispielsweise der konkrete Maschinencode hinter file_put_contents ausgeführt wird. Je nach Lust und Laune des Betriebssystems. file_put_contents beginnt zu schreiben, writeFile eines zweiten Prozesses läuft komplett durch, file_put_contents schreibt weiter. Das Resultat ist dann am Ende sicherlich für beide Prozesse völlig verkehrt.

              Das ist nur durch mutual exclusion zu verhindern. Im PHP-Core gibt es dafür mehr oder weniger nur flock.[1] PHP unterstützt zwar andere Mechanismen (Semaphoren etwa), aber die stellen zusätzliche Abhängigkeiten dar, die eine Umgebung erfüllen muss. (Das meine ich, wenn ich sage, dass es in konkreten Anwendungsfällen oftmals irgendwelche „Hacks“ gibt.) Das sind aber nicht unbedingt Abhängigkeiten, die für einfache Libraries „Sinn“ ergeben, da sie häufig exotisch oder schwergewichtig sind.

              Und parallele Schreibzugriffe solltet man mit Files in einer Webumgebung besser eh vermeiden und gleich eine Datenbank bemühen.
              Ja, tendenziell sicherlich. Aber dass es nie sinnvoll ist, direkt in Dateien zu schreiben, würde ich auch nicht sagen. Sobald das getan wird, ist die Möglichkeit der Parallelität dann quasi allgegenwärtig.

              flock sorgt da schon für Abhilfe.



              1: Die Funktion (oder wohl mehr das Betriebssystem) nutzt im Normalfall auch bloß advisory locking, das von anderem PHP-Code beachtet werden kann aber nicht zwingend beachtet werden muss. Das hängt davon ab, ob der andere Code versucht, sich ein Lock zu beschaffen oder nicht. Wenn er es nicht versucht, erhält er einfach so Zugriff. Ich gehe hier im Thread aber davon aus, dass aller Code sich an advisory locks hält.

              Kommentar


              • #8
                Ich hab dazu mal einen was kleines gemacht:

                writer.php
                PHP-Code:
                <?php
                function writeFile($filename$content) { 
                    
                $newFilename $filename ".new"
                    
                $backupFilename $filename ".bak"

                    
                file_put_contents($newFilename$content); 
                     
                    if (
                file_exists($backupFilename)) { 
                        
                unlink($backupFilename); 
                    } 
                    
                    if (
                file_exists($filename)) {
                        
                rename($filename$backupFilename); 
                    }
                    
                rename($newFilename$filename); 
                }

                $content = <<<EOT
                Lorem ipsum dolor sit amet .... gekürzt. wird dann durch str_repeat zu einer Datei mit ~300mb. 
                EOT;

                $content date("Y-m-d H:i:s") . "\n" $content;
                $content str_repeat($content100000);
                writeFile("/tmp/testfile"$content);
                reader.php
                PHP-Code:
                <?php

                $filename 
                "/tmp/testfile";

                if (!
                file_exists($filename)) {
                    die(
                "File not found");
                }

                $fp fopen($filename"r");

                if (!
                $fp) {
                    die(
                "Error reading file");
                }

                while ((
                $line fgets($fp4096))) {
                    echo 
                $line;
                    
                sleep(2);
                }
                fclose($fp);
                Dann hab ich folgendes gemacht:

                * reader.php starten.
                * /tmp/testfile löschen

                reader.php rennt dabei ohne fehler bis zum Ende durch.

                Schaut man sich dabei die geöffneten Files an sieht man folgendes:
                Code:
                php     7695 user    0u   CHR  136,1       0t0      4 /dev/pts/1
                php     7695 user    1u   CHR  136,1       0t0      4 /dev/pts/1
                php     7695 user    2u   CHR  136,1       0t0      4 /dev/pts/1
                php     7695 user    3r   REG   0,17 320200000  57076 /tmp/testfile (deleted)
                Das Betriebsystem (in dem Fall Linux mit Kernel 2.6.32) achtet beim Löschen also durchaus darauf ob es noch geöffnete Handles gibt und schiebt das dann auf bis diese das Handle freigeben. Das Script kann also mit den alten Daten wie gewohnt weiter arbeiten und kriegt im Normalfall nichts davon mit. Ob das allerdings auch unter Windows so ist, kann ich nicht sagen.

                Anders schaut es hingegen aus, wenn man in die Datei schreibt während ein anderer Prozess daraus liest. Dann erhält man einen undefinierten Inhalt, weil er einfach irgendwann dann die neuen Daten liest:
                writer2.php
                PHP-Code:
                <?php
                $filename 
                "/tmp/testfile2";
                $char "A";
                if (
                file_exists($filename)) {
                    
                $char "B";
                }

                $fp fopen($filename"w+");

                $line str_repeat($char4000) . "\n";

                for (
                $i 0$i 1000$i++) {
                    
                fputs($fp$linestrlen($line));
                    
                fflush($fp);
                }

                fclose($fp);
                reader2.php
                PHP-Code:
                <?php
                $filename 
                "/tmp/testfile2";
                if (!
                file_exists($filename)) {
                    die(
                "File not found");
                }

                $fp fopen($filename"r");
                $i 0;
                while ((
                $line fgets($fp4096))) {
                    echo (
                $i++) . ": " $line;
                    
                sleep(1);
                }

                fclose($fp);
                Da bekommt man dann irgendwo zwischen drin sowas:
                Code:
                AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
                Solange man also nicht in die Files schreibt von denen man liest hat man eigentlich kein Problem.
                Das Einzige was schiefgehen könnte ist, wenn zwischen den 2 Renames vom writer das file_exists vom reader kommen würde. Das ist allerdings ein definierter Zustand und kann entsprechend abgefangen werden.

                Grüße.

                Kommentar


                • #9
                  Danke für die Beispiele und Erläuterungen.

                  Dass Dateien tatsächlich erst gelöscht werden, wenn kein Prozess mehr auf sie zugreift, kann ich für Linux bestätigen.

                  Warum writeFile Race-Conditions verhindert, verstehe ich aber nicht.

                  write.php:

                  PHP-Code:
                  <?php

                  function writeFile($filename$content) {
                      
                  $newFilename $filename ".new";
                      
                  $backupFilename $filename ".bak";

                      
                  file_put_contents($newFilename$content);

                      if (
                  file_exists($backupFilename)) {
                          
                  unlink($backupFilename);
                      }

                      if (
                  file_exists($filename)) {
                          
                  rename($filename$backupFilename);
                      }
                      
                  rename($newFilename$filename);
                  }

                  while (
                  true) {
                      
                  writeFile('/tmp/tmpfile''AAAAAAAA');
                  }
                  read.php:

                  PHP-Code:
                  <?php

                  $i 
                  1;

                  while (
                  $i++) {
                      if (
                  file_exists('/tmp/tmpfile')) {
                          
                  $s file_get_contents('/tmp/tmpfile');
                      } else {
                          
                  $s file_get_contents('/tmp/tmpfile.bak');
                      }

                      if (
                  strlen($s) !== 8) {
                          die (
                  'ERROR ' . ($i 1));
                      }
                  }
                  Das produziert praktisch sofort eine Fehlermeldung.

                  Kommentar


                  • #10
                    Das hier läuft bei mir auch mit mehreren Writern und Readern sauber. (Um ehrlich zu sein viel sauberer, als ich erwartet hatte. Der c-Modus scheint es echt zu bringen.) Auch dann, wenn etwa DOMDocument::load und DOMDocument::save in den gelockten Codeabschnitten stehen. Ohne Locks haut das bei DOMDocument tatsächlich nicht hin.

                    PHP-Code:
                    <?php // locktest.php

                    function writer()
                    {
                        
                    $i 0;

                        while (
                    true) {
                            
                    // NICHT wb, um den Inhalt nicht zu löschen, falls das mit dem Lock
                            // nicht gleich klappt
                            
                    $h fopen('/tmp/tmpfile''cb');

                            if (
                    flock($hLOCK_EX)) {
                                
                    ftruncate($h0);
                                
                    fwrite($h'AAAAAAAA');
                                
                    flock($hLOCK_UN);
                            }

                            
                    fclose($h);

                            
                    $i++;

                            if (
                    $i 10000 === 0) {
                                echo 
                    '.';
                            }
                        }
                    }

                    function 
                    reader()
                    {
                        
                    $i 0;

                        while (
                    true) {
                            
                    $h fopen('/tmp/tmpfile''rb');

                            if (
                    flock($hLOCK_SH)) {
                                
                    $s fread($h10);
                                if (
                    $s !== 'AAAAAAAA') {
                                    die (
                    'ERROR ' . ($i 1) . ' ' $s);
                                }
                                
                    flock($hLOCK_UN);
                            }

                            
                    fclose($h);

                            
                    $i++;

                            if (
                    $i 10000 === 0) {
                                echo 
                    '.';
                            }
                        }
                    }

                    if (
                    $argc === 2) {
                        if (
                    $argv[1] === '--reader') {
                            
                    reader();
                        } elseif (
                    $argv[1] === '--writer') {
                            
                    writer();
                        }
                    }

                    // Aufruf:  $ php -f locktest.php -- --writer  bzw.
                    //          $ php -f locktest.php -- --reader

                    Kommentar


                    • #11
                      Zitat von mermshaus Beitrag anzeigen
                      PHP-Code:
                      <?php
                      while (true) {
                          
                      writeFile('/tmp/tmpfile''AAAAAAAA');
                      }
                      Hehe, dafür war's nicht gedacht. Wenn ich das so laufen lasse taucht "tmpfile" nicht mal im Dateisystem auf. Da hängt sich sogar gedit auf, der /tmp auf Änderungen überwacht und wohl zu viele Events kriegt ^^

                      Für solche Daten würde ich wohl auf memcached zurückgreifen und nicht auf Dateien.

                      Es war eher dazu gedacht...zb Cache Files....ab und zu werden sie neu erstellt, aber dauernd daraus gelesen.

                      Zitat von mermshaus Beitrag anzeigen

                      PHP-Code:
                      <?php

                      $i 
                      1;

                      while (
                      $i++) {
                          if (
                      file_exists('/tmp/tmpfile')) {
                              
                      $s file_get_contents('/tmp/tmpfile');
                          } else {
                              
                      $s file_get_contents('/tmp/tmpfile.bak');
                          }

                          if (
                      strlen($s) !== 8) {
                              die (
                      'ERROR ' . ($i 1));
                          }
                      }
                      Das produziert praktisch sofort eine Fehlermeldung.
                      Das ist genau das was ich meinte:
                      Beim file_exists gibt es tmpfile noch. Beim file_get_contents gibt es sie gerade nicht, weil gerade "rename(old, bak)" lief aber "rename(new, old)" noch nicht. Wenn du das dann natürlich tausende Male pro Sekunde hast...

                      Generell ist File-Locking halt in Web-Anwendungen sehr unschön und sollte anders gelöst werden.

                      zB hast du einen Prozess der eine Datei generiert und diese dazu 2 Sek. locked. In der Zeit könnten hunderte oder tausende Anfragen an den Server kommen die dann entweder alle 2 Sek warten müssen oder mit einem Fehler abgespeist werden. Das ist alles andere als eine Gute Lösung.

                      Grüße.

                      Kommentar


                      • #12
                        Ohne da jetzt großartig zu testen; euch gehts doch darum das zugriffskollisionen entstehen wärend man auf xy rumschreibt oder ? Insofern man Wartungsarbeiten automatisiert durchführt die z.b. einen Cache neuschreiben sollte doch der unverfänglichste Aufwand sein, wenn der Cache an 2 Punkten im Dateisystem kollisionsfrei geschrieben wird und die Anwendung nur die "neue Stelle" für das "neue File" genannt bekommt. Ein Cron der "alten Mist" wegräumt und der Lack ist geleckt.. äh.. das Problem ist gelöst.

                        Kommentar


                        • #13
                          Zitat von php1704
                          Hehe, dafür war's nicht gedacht. Wenn ich das so laufen lasse taucht "tmpfile" nicht mal im Dateisystem auf. Da hängt sich sogar gedit auf, der /tmp auf Änderungen überwacht und wohl zu viele Events kriegt ^^
                          Die CPU wird davon auch ganz gut ausgelastet. Ich habe auch überlegt, wie gut das wohl auf Dauer für meine Festplatte ist… Aber meh, muss das Boot abkönnen.

                          Es war eher dazu gedacht...zb Cache Files....ab und zu werden sie neu erstellt, aber dauernd daraus gelesen.
                          Ja, ich wollte irgendwie jeden (un-)wahrscheinlichen Fall zeitgleichen Zugriffs durchspielen. Liegt auch daran, dass ich gewisse Problem– – äh, nicht die Geduld habe, meine ich natürlich –, mir parallele Zugriffe im Kopf vorzustellen.

                          Generell ist File-Locking halt in Web-Anwendungen sehr unschön und sollte anders gelöst werden.
                          Ja, stimmt schon. Siehe auch:

                          Zitat von tr0y
                          Ohne da jetzt großartig zu testen; euch gehts doch darum das zugriffskollisionen entstehen wärend man auf xy rumschreibt oder ?
                          Ja, wobei die Sinnhaftigkeit von Locking für einen konkreten Anwendungsfall in diesem Thread nicht mein zentraler Fokus ist. Mir geht es also nicht so sehr um Alternativen. Ich sehe lediglich erst mal keinen Grund, Locking nicht zu ermöglichen. Und ein File-Handle zu öffnen und zu locken, aber dann doch file_get_contents zu nehmen, finde ich komisch, weshalb das für mich ein Argument für Streams ist.

                          Die Frage hier ist ja im Prinzip: Warum nicht Streams als Parameter nutzen statt Pfaden?

                          Kommentar


                          • #14
                            Mist, ich wollte eigentlich editieren.

                            file_put_contents kann ein Lock-Parameter übergeben werden. Und beiden Funktionen (put/get contents) ein Stream-Context. Glaube aber, so ganz viel bringt das auch nicht. Die Funktionen werden dadurch vermutlich auch nicht „lowlevel“ genug.

                            Kommentar

                            Lädt...
                            X