Pit: Per JS/Ajax auf mysql zugreifen oder wie löst man sowas?

Hallo,

ich habe ein Problem im Terminkalender. Wenn ein User einen Termin anlegt und ich will Terminüberschnedungen vermeiden, dann weiß ich (bzw. der Server) ja erst nach dem Absenden des Formulares, dass das nicht passt.

Aber nach Absenden eine Fehlermeldung finde ich nicht so hilfreich, besser wäre, bereits vor dem Absenden auf den Terminkonflikt hin zuweisen. Oder anderes Problem: In einem termin kann der User z.b. einen Raum buchen. Oder auch 2 Räume. Wie prüft man bereits clientseitig, ob da ein Terminkonflikt besteht?

Pit

  1. Tach!

    Wie prüft man bereits clientseitig, ob da ein Terminkonflikt besteht?

    Gar nicht. Das kann man nur serverseitig. (Jedenfalls solange man nicht die gesamte Datenbasis zum Client übertragen möchte.)

    Du stellst einen Request an den Server, der beantwortet ihn. Das Ergebnis wertest du aus und nimmst die entsprechenden Manipulationen im DOM vor, um es anzuzeigen. Und das alles mit Javascript. Das sind alles Teilschritte, die man erstmal unabhängig voneinander üben kann, bevor man sie zusammensetzt.

    Ja, Ajax ist das Stichwort für den ersten Teilaspekt. Herkömmlich über XMLHttpRequest, modern mit der Fetch API.

    dedlfix.

    1. Hi Dedlfix,

      danke für Deinen Beitrag. Ich glaub, das krieg ich hin.

      Aber zur serverseitigen Terminkonfliktprüfung habe ich eine oder mehr Fragen, die ich aber noch nicht ganz formuliert bekomme. ich würd deshalb da später gerne nochmal nachfragen.

      Pit

      1. Tach!

        danke für Deinen Beitrag. Ich glaub, das krieg ich hin.

        Das war erstmal nur ganz grob geantwortet. Du wirst vermutlich auf jede Menge konkrete Fragen zu Details stoßen. Nicht selten ist die Asynchronität nicht so einfach zu verstehen. Der Code steht zwar meist untereinander im Script, wird aber zeitlich in anderer Ordnung aufgerufen. Wenn man das nicht beachtet, greift man gern auf Dinge zu, die noch nicht existieren, weil man seinen vom Ajax-Call abhängigen Code nicht in dessen Callback-Funktion geschrieben hat, sondern in die Zeilen unter dem Ajax-Call.

        dedlfix.

        1. Tach!

          danke für Deinen Beitrag. Ich glaub, das krieg ich hin.

          Das war erstmal nur ganz grob geantwortet. Du wirst vermutlich auf jede Menge konkrete Fragen zu Details stoßen.

          Hi dedlfix,

          vermutlich hast Du recht 😀

          Aber zumindest mein erster Test läuft gerade gar nicht so übel.

          Ich kann Daten zum Backendscript schicken, kann sie verarbeiten und anschließend sowohl die success- als auch den error Funktion bedienen.

          Damit läßt sich ja schonmal was anfangen, oder? 😉

          Pit

    2. Hallo Pit,

      wobei man hier auf die Performance achten muss. In JavaScript ist es kein Problem, auf jeden Tastendruck des Benutzers zu reagieren und Plausibilitäten laufen zu lassen.

      Serverseitige Prüfungen sollte man zurückhaltend ausführen, Roundtrips kosten immer etwas Zeit und wenn Du viele User hast, die nach jedem Tastendruck eine Plausi abrufen, dann kann das deinen Server in die Knie zwingen. Im Falle eines Termindatums solltest Du die Prüfung also beim Verlassen des Feldes vornehmen, nicht vorher.

      Und dann hast Du das Problem der Race-Conditions. Nimm an, dass ein User einen Wert eingibt, den Cursor im Eingabefeld stehen lässt und direkt auf Speichern klickt. Damit verlässt Du das Eingabefeldfeld -> change-Event fliegt -> es startet eine Serverplausi. Das Verlassen des Eingabefeldes findet VOR dem Klick-Handling des Submit-Buttons statt, du weißt dann noch gar nicht, dass es gleich darauf mit Submit weitergeht.

      Diese Race-Condition musst Du verhindern. Da ich bisher von solchen Plausis die Finger gelassen habe, kann ich Dir dafür keine passenden Designpatterns aus der Tasche ziehen. Ich habe zwar eine Idee, aber die ist nicht erprobt und ich könnte sie hier nicht in 3 Sätzen erklären (nur als Stichworte: eine „globale Liste“ für zu erledigenden Validierungen aufbauen, in einem Submit-Event diese Queue löschen, per Promise.resolve().then() die Verarbeitung der Queue an das Ende des JavaScript Verarbeitungszyklus schieben). Global in Anführungszeichen, das kapselt man natürlich in einem Modul.

      Die Nummer ist nicht trivial, wenn man es richtig machen will. Aber vielleicht mache ich mir ja auch unnötig einen Kopf und der Browser handelt das Problem automatisch?

      Rolf

      --
      sumpsi - posui - clusi
      1. Hallo Rolf,

        wobei man hier auf die Performance achten muss. In JavaScript ist es kein Problem, auf jeden Tastendruck des Benutzers zu reagieren und Plausibilitäten laufen zu lassen.

        Das habe ich nicht vor.

        Im Falle eines Termindatums solltest Du die Prüfung also beim Verlassen des Feldes vornehmen, nicht vorher.

        Ich will eigentlich erst beim absenden des Formulars prüfen.

        Und dann hast Du das Problem der Race-Conditions.

        Der Userkreis ist recht begrenzt, aber dennoch hast Du natürlich recht. Das könnte ich doch aber in den Griff bekommen, in dem ich (über einen Schalter, wie auch immer er geartet ist), Abfragen bzw. Plausibilitätskontrollen nur nacheinander ausführe.

        Meine Vorgehensweise habe ich kurz in der Antwort zu dedlfix angerissen. Da ich mein Formular ohnehin per JS absende, kann cih einen Ajaxrequest quasi dazwischen schieben, also zwischen Klick und submit('meineForm'), sowie schließe('meineForm'). So kann ich die Antwort auswerten und entweder ins noch geöffnete Formular eine Fehlermeldung einblenden, oder den Eintrag vornehmen, ekine Erfolgsmeldung einblenden und das Formular schließen.

        Pit

        1. Tach!

          Ich will eigentlich erst beim absenden des Formulars prüfen.

          Dann lohnt es sich auch nicht mehr. Da kannst du gleich einen normalen Request nehmen und dessen Antwort anzeigen. Kommt quasi aufs selbe raus.

          Alternativ schreibst du eine SPA (Single Page Application), da kann dann der Submit ein Ajax-Request sein und die Antwort wird in die Seite eingebaut.


          Im Prinzip baust du dir bei dem Vorhaben, erst zu prüfen, dann einzutragen, ein TOCTTOU-Problem. Wenn die Anwendung gut besucht ist, könnte zwischen der Prüfung und dem Eintragen ein anderer Prozess dazwischenkommen und sich vorher eintragen.

          Deine Prüfung wird vermutlich nicht so 08/15 sein, dass du einfach blind eintragen kannst und nur auf eine Unique-Constraint-Meldung reagierst. Sowas kann man nur nehmen, um exakt doppelte Einträge zu verhindern. Bei dir sind jedoch Bereiche im Spiel, von denen nur Anfang und Ende festgehalten sind und die sich nicht überschneiden sollen. Du müsstest den Prozess der Prüfung und des anschließenden Eintragens so kapseln, dass kein anderer Prozess die betroffenen Tabellen ändern kann. Im Prinzip darf nicht mal getestet werden, weil das Ergebnis ja durch die Eintragung des anderen Prozesses sofort invalidisiert werden könnte.

          dedlfix.

          1. Hallo dedlfix,

            TOCTTOU-Problem

            Verdammt, diese Abk. musste ich googeln 😀 -> https://de.wikipedia.org/wiki/Time-of-Check-to-Time-of-Use-Problem

            Es wäre aber doch wohl ohnehin selbstverständlich, dass man Eingabe begleitende Plausis nur des Komforts wegen macht. Die einzige Prüfung, die wirklich relevant ist, ist die beim Submit.

            Rolf

            --
            sumpsi - posui - clusi
            1. Tach!

              Es wäre aber doch wohl ohnehin selbstverständlich, dass man Eingabe begleitende Plausis nur des Komforts wegen macht. Die einzige Prüfung, die wirklich relevant ist, ist die beim Submit.

              Auch da kann es zum TOCTTOU kommen, wenn es der Vorgang mehrteilig ist und die Chance besteht, dass andere Prozesse gleichzeitig dasselbe wollen.

              dedlfix.

              1. Auch da kann es zum TOCTTOU kommen, wenn es der Vorgang mehrteilig ist und die Chance besteht, dass andere Prozesse gleichzeitig dasselbe wollen.

                Absolut. Ist zwar ein sehr geringer Gefahrenbereich, aber wenn er zuschlägt, ist auch auch besonders schwer zu identifizieren und eventuell verursachten Datensalat zu rekonstruieren. Vor solchen Fehlern habe ich echt Sorge..

                Pit

              2. Hallo dedlfix,

                das ist richtig, aber bei der Verarbeitung des Submit habe ich es in der Hand, über entsprechende DB-Locks (sprich: Transaktion und Isolation Level) die anderen auszusperren. Damit tausche ich dann zwar ggf. den TOCTTOU gegen einen DEADLOCK ein, aber letzteren sollte man mittels passender Update-Reihenfolge lösen können.

                Wenn man alle Update-Prozesse selbst in der Hand hat, gibt es auch die Möglichkeit, über einen Semaphor logische Locks zu erschaffen, die keine DB-Locks sind (sprich: eine DB-Tabelle, in der für ein "Thema" ein Besitzer gesetzt wird, dann werden alle Updates für das Thema gemacht und zum Abschluss das Thema wieder freigegeben. Die Themen müssen natürlich so gewählt werden, dass sich die Updates zweier Themen nicht überschneiden können. Wenn doch, muss für die Schnittmenge ein eigenes Thema definiert werden und der Updater muss sich mehrere Themen reservieren, bevor er loslegt. Setzen und Löschen des Themas sind kurzlaufende Aktionen und lassen sich mittels optimistischem Sperrverfahren ohne DB-Locks umsetzen (Mehrfachthemen natürlich nicht, dafür braucht's wieder eine Transaktion). Ist natürlich kritisch, wenn das Script beim Owner abbricht, weil dann der Semaphor gesetzt bleibt, da muss man entsprechende Exception-Fänger installieren und ggf. eine Logik einbauen, die einen "zu alten" Semaphor als "vergessen" betrachtet und überschreibt.

                Rolf

                --
                sumpsi - posui - clusi
                1. Tach!

                  das ist richtig, aber bei der Verarbeitung des Submit habe ich es in der Hand, über entsprechende DB-Locks (sprich: Transaktion und Isolation Level) die anderen auszusperren.

                  Genau das meine ich, dass man da mit Locking arbeiten müsste.

                  dedlfix.

                  1. Hallo dedlfix,

                    ok ok :)

                    Habe noch was nachgetragen nachdem Du gepostet hast.

                    Rolf

                    --
                    sumpsi - posui - clusi
          2. Hi dedlfix,

            Ich will eigentlich erst beim absenden des Formulars prüfen.

            Dann lohnt es sich auch nicht mehr. Da kannst du gleich einen normalen Request nehmen und dessen Antwort anzeigen. Kommt quasi aufs selbe raus.

            Ich weiß, was Du meinst. Da mein Formular aber ein Dialogfenster ist, macht es in meinem Fall doch noch einen kleinen Sinn. Zudem sende ich (wenn auch nicht viel) weniger Daten im Falle eines Konflikts.

            Alternativ schreibst du eine SPA (Single Page Application), da kann dann der Submit ein Ajax-Request sein und die Antwort wird in die Seite eingebaut.

            Aber so wird es doch auch in meinem Dialog gemacht.

            Im Prinzip baust du dir bei dem Vorhaben, erst zu prüfen, dann einzutragen, ein TOCTTOU-Problem. Wenn die Anwendung gut besucht ist, könnte zwischen der Prüfung und dem Eintragen ein anderer Prozess dazwischenkommen und sich vorher eintragen.

            Stimmt. Aber es ist nicht davon auszugehen, dass die Anwendung gut besucht ist. Es ist ein klar definierter, abgegrenzter Userkreis.

            Deine Prüfung wird vermutlich nicht so 08/15 sein, dass du einfach blind eintragen kannst und nur auf eine Unique-Constraint-Meldung reagierst.

            Korrekt.

            Bei dir sind jedoch Bereiche im Spiel, von denen nur Anfang und Ende festgehalten sind und die sich nicht überschneiden sollen. Du müsstest den Prozess der Prüfung und des anschließenden Eintragens so kapseln, dass kein anderer Prozess die betroffenen Tabellen ändern kann. Im Prinzip darf nicht mal getestet werden, weil das Ergebnis ja durch die Eintragung des anderen Prozesses sofort invalidisiert werden könnte.

            Das sehe ich ganz genauso. Ich mach mal und melde mich bei Fragen wieder bzw. meld mal, wenns fertig ist.

            Danke für Deine (und auch Rolfs) Hinweise,

            Pit