Take: Dateisperren im Dauereinsatz

Moin,

ich mal wieder.
Ich schreibe grade an einer Webseite in PHP. Diverse Einstellungen, die im Frontend der Seite gemacht werden können, speichere ich in einer JSON Datei.
Meine Frage ist nun: wie gehe ich da am Besten vor? Ich habe mir eine File Klasse gebastelt die den Datei-IO regelt. Im Moment mache ich es so, dass ich einfach im Konstruktor die Datei mit LOCK_EX sperre, denn es könnten ja auch Schreibzugriffe stattfinden. Im Destruktor wird die Sperre wieder aufgehoben. Kann man das so machen? Irgendwie ist mir nämlich nicht so wohl dabei, aber ist das Premature Optimization?

Einige Ideen die ich hatte:
* Erstmal mit LOCK_SH sperren, erst auf LOCK_EX wechseln wenn wirklich ein Schreibzugriff kommt. Das Problem dabei: Angenommen ich lese Informationen ein (es wird erstmal LOCK_SH geholt) und schreibe sie dann verändert wieder zurück (erst dann mit LOCK_EX). Was passieren kann:

  1. Programm A liest ein
  2. Programm B liest ein (geht, weil Prgm. A nur LOCK_SH hält)
  3. Programm A versucht zurückzuschreiben mit LOCK_EX, muss aber auf B warten, da B LOCK_SH hält
  4. Programm B macht das gleiche => Deadlock.

* Im Konstruktor mit angeben, ob die Datei auch zum Schreiben geöffnet werden soll. Problem: erfordert mehr Logik in der Klasse die die Einstellungen speichert.

Für jedwede Meinungen und Anregungen bin ich dankbar! :)

Gruß,
Take

  1. Tach!

    Ich schreibe grade an einer Webseite in PHP. Diverse Einstellungen, die im Frontend der Seite gemacht werden können, speichere ich in einer JSON Datei.

    Warum JSON und nicht PHPs eigenes und auf die vorhandenen Typen spezialisiertes serialize()/unserialize()? Warum überhaupt dateibasiert und nicht mit SQLite? Mit SQLite hättest du die Sperr-Sorgen nicht, das kümmert sich selbst darum.

    Meine Frage ist nun: wie gehe ich da am Besten vor? Ich habe mir eine File Klasse gebastelt die den Datei-IO regelt.

    Warum eine Klasse? file_get_contents() und file_put_contents() erledigt den Zugriff in einem Rutsch. Für das Schreiben kennt file_put_contents() auch ein Flag, das die Sperre setzt.

    Ansonsten kann ich zu Dateisperren nicht mehr sagen, als auf Sperren von Dateien verweisen.

    dedlfix.

    1. Moin,

      Warum JSON und nicht PHPs eigenes und auf die vorhandenen Typen spezialisiertes serialize()/unserialize()? Warum überhaupt dateibasiert und nicht mit SQLite? Mit SQLite hättest du die Sperr-Sorgen nicht, das kümmert sich selbst darum.

      1. JSON vs serialize: Von mir aus auch gerne serialize/unserialize. Problem bleibt. Allerdings ist mir JSON lieber, denn JSON ist ein standardisiertes Format, praktisch alles und jeder kann damit umgehen. Mit PHP serialize habe ich das nicht.

      Warum eine Klasse? file_get_contents() und file_put_contents() erledigt den Zugriff in einem Rutsch. Für das Schreiben kennt file_put_contents() auch ein Flag, das die Sperre setzt.

      1. Eine Klasse weil ich OOP programmiere. Eine Datei ist ein Objekt => eine Klasse. Die hat Methoden wie size() oder isWritable().

      Mit file_***_contents habe ich folgendes Problem:

      1. Programm A liest ein (ich nehme an file_get_contents setzt LOCK_SH) und arbeitet mit den Daten.
      2. Programm B liest ein
      3. Programm A schreibt die veränderten Daten zurück
      4. Programm B schreibt die veränderten Daten zurück => Veränderungen von A sind weg

      Ansonsten kann ich zu Dateisperren nicht mehr sagen, als auf Sperren von Dateien verweisen.

      Kenn ich schon :)

      Gruß,
      Take

      1. Tach!

        1. JSON vs serialize: Von mir aus auch gerne serialize/unserialize. Problem bleibt. Allerdings ist mir JSON lieber, denn JSON ist ein standardisiertes Format, praktisch alles und jeder kann damit umgehen. Mit PHP serialize habe ich das nicht.

        Wozu brauchst du die Standardisierung? Soll es ein Projekt werden, bei dem man von mehreren Plattformen aus auf die Daten zugreifen muss? Wenn du derzeit nur PHP verwendest und erstmal nichts weiter planst, wäre eine Standardisierung nur YAGNI.

        Warum eine Klasse? file_get_contents() und file_put_contents() erledigt den Zugriff in einem Rutsch. Für das Schreiben kennt file_put_contents() auch ein Flag, das die Sperre setzt.

        1. Eine Klasse weil ich OOP programmiere. Eine Datei ist ein Objekt => eine Klasse. Die hat Methoden wie size() oder isWritable().

        Und das brauchst du alles für die simple Aufgabe der Konfigurationsdatenspeicherung? Oder willst du nur einfach aus Spaß an der Freude und der Vollständigkeit halber - auch wenn es derzeit nicht benötigt wird - eine Full-Blown-Dateiverwaltungsklasse schreiben?

        Mit file_***_contents habe ich folgendes Problem: [TOCTTOU]

        Das stimmt natürlich. Deswegen ja auch lieber SQLite.

        dedlfix.

        1. Moin,

          Wozu brauchst du die Standardisierung? Soll es ein Projekt werden, bei dem man von mehreren Plattformen aus auf die Daten zugreifen muss? Wenn du derzeit nur PHP verwendest und erstmal nichts weiter planst, wäre eine Standardisierung nur YAGNI.

          YAGNI in allen Ehren, aber ob ich nun serialize statt json_encode und deserialize statt json_decode schreibe tut sich doch nichts, oder? Und außerdem ist JSON schöner zu lesen beim Debuggen.

          Und das brauchst du alles für die simple Aufgabe der Konfigurationsdatenspeicherung? Oder willst du nur einfach aus Spaß an der Freude und der Vollständigkeit halber - auch wenn es derzeit nicht benötigt wird - eine Full-Blown-Dateiverwaltungsklasse schreiben?

          Was an einer Wrapper-Klasse über die file_*** / f*** funktionen "Full-Blown" sein soll erschließt sich mir jetzt nicht.

          Das stimmt natürlich. Deswegen ja auch lieber SQLite.

          Was nützt mir da SQLite? Gleiches Problem:

          1. Programm A liest aus
          2. Prgramm B liest aus
          3. Programm A schreibt
          4. Programm B schreibt

          Ich muss vllt. nicht mehr selbst locken, aber das ändert nichts am Problem. Das File-Locking an sich ist nicht mein Problem.

          Gruß,
          Take

          1. Tach!

            YAGNI in allen Ehren, aber ob ich nun serialize statt json_encode und deserialize statt json_decode schreibe tut sich doch nichts, oder? Und außerdem ist JSON schöner zu lesen beim Debuggen.

            Ich versuche nicht, dich zu ärgern, ich will nur herausfinden, ob du dir genügend Gedanken über alternative Wege gemacht hast, beziehungswiese diese gegebenenfalls anzuregen.

            Was an einer Wrapper-Klasse über die file_*** / f*** funktionen "Full-Blown" sein soll erschließt sich mir jetzt nicht.

            Du erwähntest Funktionalität, die auf den ersten Blick für das Problem der Konfigurationsdatenspeicherung unnötig erscheint. Deswegen mein Verdacht, dass du erstmal eine ausgewachsene und universelle Wrapper-Klasse für PHPs Dateisystemfunktionen schreiben willst.

            Ich muss vllt. nicht mehr selbst locken, aber das ändert nichts am Problem. Das File-Locking an sich ist nicht mein Problem.

            Es hilft dir nicht einmal, das eigentliche Problem webbasierter Bearbeitungen zu lösen. Ich denke, wir lassen die Suche nach Alternativen auf sich beruhen und schauen mal im anderen Zweig, wie das Problem zu lösen geht.

            dedlfix.

          2. Hi there,

            Was an einer Wrapper-Klasse über die file_*** / f*** funktionen "Full-Blown" sein soll erschließt sich mir jetzt nicht.

            Weil als nächstes dann irgendwann die Wrapper-Klasse der Wrapper-Klasse eingeführt wird usw. usf.
            Wenn ich eine (noch dazu native) Funktion habe, dann verwende ich diese einfach und gut ists. Programmieren hat mit Schönheit nichts zu tun sondern mit Funktionalität und Effizienz. Oder anders 'rum: Ich kenne genügend Programme, die in Schönheit gestorben sind, aber kein einziges, das in Funktionalität verendet ist...

  2. hi,

    Meine Frage ist nun: wie gehe ich da am Besten vor? Ich habe mir eine File Klasse gebastelt die den Datei-IO regelt.

    Das ist schonmal ne gute Idee, den Zugriff auf persistente Daten zu abstrahieren (s. Anm.).

    Für jedwede Meinungen und Anregungen bin ich dankbar! :)

    Im Programm operierst Du nur mit einer Datenstruktur über Deine Klasse/Interface zur Datei/Beliebiger Speicherort. Wenn Du den Prozess atomar haben willst (atomar: Nicht teilbar), setze LOCK_EX im Konstruktor Deiner Klasse und hebe das LOCK_EX im Destruktor wieder auf.

    Die Datenstruktur könnte ein Array sein ;)

    Anm.: Deine Klasse wird austauschbar. Wenn Du später eine DB zum Speichern persistenter Daten hast, muss Dein Code nicht geändert werden, es wird nur die Klasse/Interface ausgetauscht.

    Hotti

    1. Moin,

      Im Programm operierst Du nur mit einer Datenstruktur über Deine Klasse/Interface zur Datei/Beliebiger Speicherort.

      Exakt das habe ich. Ein Interface Configuration, das praktisch eine key=>value Beziehung macht. Dann gibts die Klasse JsonConfiguration die das mit einem Array umsetzt und dieses in eine Json Datei speichert.

      Wenn Du den Prozess atomar haben willst (atomar: Nicht teilbar), setze LOCK_EX im Konstruktor Deiner Klasse und hebe das LOCK_EX im Destruktor wieder auf.

      Das tue ich bereits. Meine Frage war, ob das so OK ist. Denn es kann durchaus sein, dass ein Aufruf des Programms die Daten nicht ändert, sondern nur ausliest. Dann bräuchte ich ja nur LOCK_SH.

      Anm.: Deine Klasse wird austauschbar. Wenn Du später eine DB zum Speichern persistenter Daten hast, muss Dein Code nicht geändert werden, es wird nur die Klasse/Interface ausgetauscht.

      Das war der Gedanke :)

      Gruß,
      Take

      1. hi,

        Das tue ich bereits. Meine Frage war, ob das so OK ist.

        Klar. Mit einem LOCK_EX über den gesamten Prozess bist Du auf der sicheren Seite!

        Denn es kann durchaus sein, dass ein Aufruf des Programms die Daten nicht ändert, sondern nur ausliest. Dann bräuchte ich ja nur LOCK_SH.

        Das wäre dann die Optimierung, da musst Du selber ins Detail gehen und genau hingucken. Es kommt auf den konkreten Anwendungsfall an. Wenn Deine Klasse/Interface die Bezeichnung 'abstrakt' (bitte nicht verwechseln mit 'abstrakter Klasse') nicht verlieren soll, wirst Du ein 'LOCK_EX' nicht im Programmcode notieren.

        Vorschlag:

          
        // im Programm  
        $db = new Datenbunker;               // nur lesende Zugriffe  
        $db = new Datenbunker ('lock' => 1); // RW atomar  
        
        

        Hotti

        1. Moin,

          Klar. Mit einem LOCK_EX über den gesamten Prozess bist Du auf der sicheren Seite!

          Das ist mir klar. Aber es kostet natürlich Performance. Der Großteil der Zugriffe werden nur-lese Zugriffe sein.

          Das wäre dann die Optimierung, da musst Du selber ins Detail gehen und genau hingucken. Es kommt auf den konkreten Anwendungsfall an. Wenn Deine Klasse/Interface die Bezeichnung 'abstrakt' (bitte nicht verwechseln mit 'abstrakter Klasse') nicht verlieren soll, wirst Du ein 'LOCK_EX' nicht im Programmcode notieren.

          Vorschlag:

          // im Programm
          $db = new Datenbunker;               // nur lesende Zugriffe
          $db = new Datenbunker ('lock' => 1); // RW atomar

          Hatte ich mir gedacht, das es auf soetwas hinausläuft. Dann aber eher:  
          ~~~php
            
          interface Config {  
              function get($key);  
          }  
          interface WritableConfig extends Config {  
              function set($key);  
          }
          

          Vielen Dank für die Hilfe.

          Gruß,
          Take

          1. hi,

            Hatte ich mir gedacht, das es auf soetwas hinausläuft. Dann aber eher:

            interface Config {
                function get($key);
            }
            interface WritableConfig extends Config {
                function set($key);
            }

              
            Klaro, geht auch so ;)  
              
            Eine krass andere Lösung sähe z.B. so aus: Deine DALs benutzen gar kein Lock. Wenn ein Prozess locken muss, wird in der Anwendungsklasse einfach ein  
              
            `$this->LOCK = 1;`{:.language-php}  
              
            gesetzt. PHP Magic Methods laden, wie von Geisterhand bewegt, eine Klasse 'LOCK' als Attribut in die eigene Instanz. In Class 'LOCK' wird auf eine Ressource mit einem konstanten Pfadnamen das LOCK\_EX gesetzt und erst wieder aufgehoben, wenn die Instanz Deiner Klasse stirbt :D  
              
            Hotti
            
            1. Moin,

              Eine krass andere Lösung sähe z.B. so aus: Deine DALs benutzen gar kein Lock. Wenn ein Prozess locken muss, wird in der Anwendungsklasse einfach ein

              $this->LOCK = 1;

              gesetzt. PHP Magic Methods laden, wie von Geisterhand bewegt, eine Klasse 'LOCK' als Attribut in die eigene Instanz. In Class 'LOCK' wird auf eine Ressource mit einem konstanten Pfadnamen das LOCK_EX gesetzt und erst wieder aufgehoben, wenn die Instanz Deiner Klasse stirbt :D

              Klingt ja ganz cool, aber löst wieder das Problem nicht :P Denn auch hier können 2 Verschiedene Programm-Instanzen die Datei einlesen, bevor eine von beiden mit LOCK_EX sperrt.

              Gruß,
              Take

              1. hi,

                Klingt ja ganz cool, aber löst wieder das Problem nicht :P Denn auch hier können 2 Verschiedene Programm-Instanzen die Datei einlesen, bevor eine von beiden mit LOCK_EX sperrt.

                Genau dieses Problem machst Du Dir selbst, wenn Lesen+Schreiben teilbar ist:

                Wenn ein Prozess, der selbst schreiben will, vorher die Quelle lesen kann, bevor ein anderer, schreibender Prozess mit dem Schreiben fertig ist, dann ist die Konsistenz Deiner Daten nicht mehr sichergestellt.

                Hotti

  3. Tach!

    Ich nochmal, und etwas genauer aufs Problem geschaut.

    Einige Ideen die ich hatte:
    * Erstmal mit LOCK_SH sperren, erst auf LOCK_EX wechseln wenn wirklich ein Schreibzugriff kommt. Das Problem dabei: Angenommen ich lese Informationen ein (es wird erstmal LOCK_SH geholt) und schreibe sie dann verändert wieder zurück (erst dann mit LOCK_EX). Was passieren kann:

    1. Programm A liest ein
    2. Programm B liest ein (geht, weil Prgm. A nur LOCK_SH hält)
    3. Programm A versucht zurückzuschreiben mit LOCK_EX, muss aber auf B warten, da B LOCK_SH hält
    4. Programm B macht das gleiche => Deadlock.

    Und das selbst, wenn A und B verschiedene Teile der Datei ändern wollen. Einige DBMS können datensatzbasiert sperren, da passiert sowas nicht. Das Problem muss doch aber schon gelöst sein, denn Dateisperren existieren ja nicht erst seit gestern. Nach meinen Überlegungen muss der Prozess, der von SH kommend ein EX anfordert, ein Timeout haben. Wenn dieses eintritt, muss die gesamte Aktion für gescheitert erklärt werden, inklusive dass die Daten, die vorher aus der Datei gelesen wurden, ungültig werden. Es muss dann von vorn begonnen werden.

    Das Problem ist jedoch eigentlich noch weitreichender, selbst mit Datenbank. Webbasierte Bearbeitung findet ja in mehreren Prozessen statt. Aufgrund eines Requests von A liest ein Prozess die Daten, erzeugt eine Response mit dem Formular, und beendet sich (natürlich unter Verlust aller Sperren). Nun hat der Anwender A fertig und will die Daten schreiben, aber Anwender B war schneller, und wenn A im selben Bereich geändert hat, sind Bs Änderungen gefährdet. Dieses TOCTTOU-Problem zu lösen, geht mit Dateisperen allein nicht mehr. Da braucht es auch noch einen Zeitstempel oder ein ähnliches eindeutiges Merkmal (Checksum), um die Originalität der Daten vor dem Ändern zu prüfen.

    dedlfix.

    1. Moin,

      Und das selbst, wenn A und B verschiedene Teile der Datei ändern wollen. Einige DBMS können datensatzbasiert sperren, da passiert sowas nicht. Das Problem muss doch aber schon gelöst sein, denn Dateisperren existieren ja nicht erst seit gestern. Nach meinen Überlegungen muss der Prozess, der von SH kommend ein EX anfordert, ein Timeout haben. Wenn dieses eintritt, muss die gesamte Aktion für gescheitert erklärt werden, inklusive dass die Daten, die vorher aus der Datei gelesen wurden, ungültig werden. Es muss dann von vorn begonnen werden.

      Dieser Ansatz hört sich für mich gut an. Einziges Problem ist, dass dann das komplette Programm auf "von vorn beginnen" eingerichtet werden muss. Denn mit den eingelesenen Daten geschieht ja etwas, sie werden im ganzen Programm verteilt.

      Das Problem ist jedoch eigentlich noch weitreichender, selbst mit Datenbank. Webbasierte Bearbeitung findet ja in mehreren Prozessen statt. Aufgrund eines Requests von A liest ein Prozess die Daten, erzeugt eine Response mit dem Formular, und beendet sich (natürlich unter Verlust aller Sperren). Nun hat der Anwender A fertig und will die Daten schreiben, aber Anwender B war schneller, und wenn A im selben Bereich geändert hat, sind Bs Änderungen gefährdet. Dieses TOCTTOU-Problem zu lösen, geht mit Dateisperen allein nicht mehr. Da braucht es auch noch einen Zeitstempel oder ein ähnliches eindeutiges Merkmal (Checksum), um die Originalität der Daten vor dem Ändern zu prüfen.

      Dieses Problem ist mir durchaus bewusst. Es liegt allerdings mMn. auf einem höheren Abstraktionslayer. Beginnt ein Benutzer eine Datei zu editieren, wird ihm der Zeitstempel des letzten Edits mitgegeben. Wenn er speichert, wird anhand des Zeitstempels (oder einer Versionsnummer, etc.) überprüft, ob die Datei inzwischen noch einmal geändert wurde. Wenn ja, bekommt der Benutzer eine Fehlermeldung und den Hinweis, er solle doch bitte seine Änderungen an die neue Version der Datei anpassen.
      Dieser Mechanismus erfordert allerdings die Lösung des von mir angesprochenen Problems mit Dateisperren, oder?

      Gruß,
      Take

      1. Tach!

        Einziges Problem ist, dass dann das komplette Programm auf "von vorn beginnen" eingerichtet werden muss. Denn mit den eingelesenen Daten geschieht ja etwas, sie werden im ganzen Programm verteilt.

        Vermutlich nicht komplett. Ich weiß nicht, was genau du mit den Daten anstellst, aber meist ist es doch nur ein Lesen für einen einzigen Request mit anschließendem Verwerfen neben allen anderen Daten am Requestende. Und wieder nur ein Lesen für den nächsten Request, usw. Ändern kommt üblicherweise nur recht selten vor. Das "von vorn beginnen" beschränkt sich also auf den Fall, das jemand was ändern will.

        Webbasierte Bearbeitung findet ja in mehreren Prozessen statt. [...] Dieses TOCTTOU-Problem zu lösen, geht mit Dateisperen allein nicht mehr. Da braucht es auch noch einen Zeitstempel oder ein ähnliches eindeutiges Merkmal (Checksum), um die Originalität der Daten vor dem Ändern zu prüfen.
        Dieses Problem ist mir durchaus bewusst. Es liegt allerdings mMn. auf einem höheren Abstraktionslayer.

        Ja, aber diesen Fall musst du am Ende auch noch lösen, zusätzlich zu den Parallelitätsproblemen beim eigentlichen Datei-Schreibvorgang.

        Beginnt ein Benutzer eine Datei zu editieren, wird ihm der Zeitstempel des letzten Edits mitgegeben. Wenn er speichert, wird anhand des Zeitstempels (oder einer Versionsnummer, etc.) überprüft, ob die Datei inzwischen noch einmal geändert wurde. Wenn ja, bekommt der Benutzer eine Fehlermeldung und den Hinweis, er solle doch bitte seine Änderungen an die neue Version der Datei anpassen.
        Dieser Mechanismus erfordert allerdings die Lösung des von mir angesprochenen Problems mit Dateisperren, oder?

        Nur für den eigentlichen Schreibvorgang. Du musst ja selbst für die Zeitstempelprüfung (und dem potentiellen nachfolgenden Schreiben) nach dem Formularabsenden sicherstellen, dass nicht innerhalb deines Prozesses ein anderer Prozess querschießt.

        Datenbankbasiert könnte man ein UPDATE ..., timestamp=NOW() WHERE id=$id AND timestamp=$old_timestamp loslassen. Wurde dann ein Datensatz geändert, war alles bestens und man ist schon fertig. Nur bei 0 affected rows gab es ein Problem mit zwischenzeitlicher Fremdbearbeitung. UPDATE ist ein atomarer Prozess, dafür sorgt das DBMS, da muss man sich nicht noch um weitere Sperren kümmern, die zwischen Lesen zwecks Ermitteln des aktuellen Timestamps für die Prüfung und Schreiben eine Parallelbearbeitung verhindern. Letzteres musst du aber bei dateibasiertem System mit Dateisperren selbst implementieren. Damit hast du dann zwei Abbruchbedingungen: einmal der geänderte Timestamp und dann das Misslingen des Schreibens, weil der EX nicht nahtlos nach dem SH zu bekommen war.

        dedlfix.

        1. Moin,

          so, hab jetzt mal nen Spaziergang an der frischen Luft gemacht, kann ganz schön helfen :)
          Folgende Idee:

          Zusätzlich zu jeder betroffenen Datei (foo.json) gibt es eine 2te Datei foo.json.version. In dieser steht lediglich eine Zahl, die bei jedem Schreiben inkrementiert wird.
          Folgendes Vorgehen:
          Beim Öffnen der Datei (egal in welchem Modus) wird automatisch immer die aktuelle Version mitgelesen.

          Wenn dann zurückgeschrieben wird, wird nach dem erlangen von LOCK_EX erstmal überprüft, ob die Versionsnummer noch stimmt. Wenn nicht, wird eine FileModifiedException (o.ä.) geschmissen, derjenige der die File Klasse benutzt muss dann damit umgehen (im Falle einer Config die Config neu laden und dann die Änderungen speichern, etc.).

          Dadurch kann ich sogar folgendes machen: Datei mit LOCK_SH sperren, einlesen, Sperre freigeben. Dann Daten modifizieren und wie oben zurückschreiben.

          Klingt gut?

          Gruß,
          Take

          1. Tach!

            Beim Öffnen der Datei (egal in welchem Modus) wird automatisch immer die aktuelle Version mitgelesen.

            Es bleibt ein Schritt, wenn die Versionsnummer in ihr drin steht, denn ...

            Wenn dann zurückgeschrieben wird, wird nach dem erlangen von LOCK_EX erstmal überprüft, ob die Versionsnummer noch stimmt.

            ... wenn du das EX bekommst, kannst du auch in der Datei selbst nach der Versionsnummer suchen.

            Dadurch kann ich sogar folgendes machen: Datei mit LOCK_SH sperren, einlesen, Sperre freigeben. Dann Daten modifizieren und wie oben zurückschreiben. Klingt gut?

            Die Datei loslassen must du ja auch zwischen zwei Requests. Also muss es auch problemlos möglich sein, innerhalb eines Requests die Datei loszulassen. Hauptsache, du bekommst keine Deadlocks. Also beim Schreiben solltest du gleich EX holen und nicht mit SH andere auf EX wartende SH blockieren, während du selbst auf EX wartest.

            dedlfix.

      2. Hello,

        Dieses Problem ist mir durchaus bewusst. Es liegt allerdings mMn. auf einem höheren Abstraktionslayer. Beginnt ein Benutzer eine Datei zu editieren, wird ihm der Zeitstempel des letzten Edits mitgegeben. Wenn er speichert, wird anhand des Zeitstempels (oder einer Versionsnummer, etc.) überprüft, ob die Datei inzwischen noch einmal geändert wurde. Wenn ja, bekommt der Benutzer eine Fehlermeldung und den Hinweis, er solle doch bitte seine Änderungen an die neue Version der Datei anpassen.

        Ich habe dazu mal ein Beispiel verbrochen, das immer noch im Netz steht. Da wid der Vereinfachung halber aber die ganze Datei abgefragt auf Änderung und nicht der diskrete Datensatz. Für den wahren Betrieb müsste man die Conflict-Counter auf Satzebene odre sogar bis auf Feldebene hinunter tragen. Eine Datenbank (Client-Server), die das auf Satzebene schon 1993 so gemacht hat, ist bTrieve (Später Pervasive Computing).

        Ich habe das Beispiel allerdings mit Filefunktionen aufgebaut. Guckst Du hier und spielst vielleicht auch mal im konkurrierenden Betrieb:

        http://selfhtml.bitworks.de/artikel_locking/adressen.php
        http://selfhtml.bitworks.de/artikel_locking/adressen.php.txt

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://restaurant-zur-kleinen-kapelle.de