Announcement

Collapse
No announcement yet.

FB Isolation/Übertragungskonzept

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • FB Isolation/Übertragungskonzept

    Guten Tag,
    ich bin leider gerade über ein Problem bei der Konzeption eines neues Projektes gestoßenl, mit dem ich selbst nicht ganz klar komme.

    Es sollte Firebird 2.1 + IBO unter Delphi7 zum Einsatz kommen.

    Ich möchte einmal kurz den Ablauf schildern:
    Es geht darum große Datenmengen an ~250 Clients zu schicken.
    Dabei können alle paar Sekunden Änderungen in der Datenmenge auftauchen, welche Schnellstmöglichst zu den Clients muss.
    Immer die komplette Datenmenge zu selektieren und an die Clients zu schicken ist weder per "Polling" und selbst mit "Events" nicht praktikabel.
    Einige Testläuft zeigten das im Prinzip die Datenbank in die Knie'e geht / die Query/Übertragung selbst zu lange dauert.

    Es reicht allerdings auch nur die Änderungen zu senden, welche wesentlich kleiner sind! Gut. Ausgedacht, umgesetzt und getestet.
    Soweit so gut. Bis ich dann auf ein "kleines" Problem gestoßen bin, welchem ich wohl ohne Hilfe nicht gewachsen bin

    Nun zum Problem:
    Ich habe das ganze über einen Before-Update Trigger umgesetzt, der bei der Änderung eines Datensatzes einen Timestamp protokolliert. Die Clients wissen welchen Datenbestand ( "zeitlichen") sie lokal haben und requesten immer alle Änderungen seit der letzten Aktualisierung.

    Nun wird aber der Before-Update Timestamp beim ausführen des Update-SQLs gesetzt. Zu diesem Zeitpunkt ist die Transaktion unter Umständen NICHT commited. Beim Commit wird dann der - zu diesem Zeitpunkt - "alte Timestamp" als letztes Änderungsdatum vermerkt.

    Dadurch kann - je nach Zeitraum zwischen dem Update und dem Commit - eine große Zeitlücke entstehen in der die Änderung nicht erkannt wird. Dieser Fall ist fatal und nicht tragbar :-)

    D.h. hier nochmal ein Ablauf-Bsp:

    Zeitpunkt 1: Client holt sich alle Daten ab.
    Zeitpunkt 2: Server ändert Datensatz. (noch kein Commit)
    Zeitpunkt 3: Client holt sich alle Änderungen seit Zeitpunkt 1. (findet Änderung 2 noch nicht, da noch nicht commited) und glaubt er hat alle Daten bis Zeitpunkt 3.
    Zeitpunkt 4: Server commited (Schreibt Zeitpunkt 2! als Änderungsdatum).
    Zeitpunkt 5: Client holt sich alle Änderungen seit Zeitpunkt 3. (Ließt die Änderung nicht, da Sie mit Zeitpunkt 2 eingetragen ist)

    --> Damit wurde die Änderung von Zeitpunkt 2 vom System verschluckt.


    Lösungsansatz:
    Ich muss den Timestamp des Commit's setzen, und nicht den Zeitpunkt der Änderung. Hier habe ich aber leider keine Ahnung wie, denn im After-Update Trigger kann ich ja keinen Wert des Datensatzes in Firebird 2.1 mehr ändern.
    Den Timestamp manuell nach dem Update zu setzen ist aufgrund der massiven größe des restlichen Systems stark fragwürdig.



    Bin für alle Ideen sehr dankbar :-)
    Hoffe dazu fällt jemanden etwas ein, bin mittlerweile etwas ratlos :[
    Zuletzt editiert von Trynn; 09.11.2009, 22:58.

  • #2
    Nur mal ein Gedankenspiel kurz vor Mitternacht ...
    Ich habe im Datensatz ein BigInteger (keine Ahnung, wie das bei FB heisst) und dazu einen Identity-Generator, der bei jeder Neuanlage/Änderung diesen Wert neu setzt. So könnte ein Client zB seine ihm bekannte größte ID ermitteln, sagen wir mal die sei 800, dann würde er für das Update alle Records mit ID > 800 vom Server berücksichtigen müssen. Das Ganze je Tabelle, dann reicht vielleicht auch ein Standard-Int. Um das jetzt konkret durchzudenken ist es mir zu spät, aber vielleicht hilft es ja trotzdem.

    bye,
    Helmut

    Comment


    • #3
      Originally posted by Trynn View Post
      Es geht darum große Datenmengen an ~250 Clients zu schicken.
      was ist deine Vorstellung von schicken? Wie groß ist die Datenmenge? Kann man davon ggf Daten schon beim client lassen (z.B. über Replikation)?

      Originally posted by Trynn View Post
      Dabei können alle paar Sekunden Änderungen in der Datenmenge auftauchen, welche Schnellstmöglichst zu den Clients muss.
      das geht ja wie du schon weisst am besten per Event


      Originally posted by Trynn View Post
      Immer die komplette Datenmenge zu selektieren und an die Clients zu schicken ist weder per "Polling" und selbst mit "Events" nicht praktikabel.
      Einige Testläuft zeigten das im Prinzip die Datenbank in die Knie'e geht / die Query/Übertragung selbst zu lange dauert.
      Replikation ist bei 250 Usern sicherlich effektiver, entspricht ja auch deiner folgenden Erkenntnis


      Originally posted by Trynn View Post
      Es reicht allerdings auch nur die Änderungen zu senden, welche wesentlich kleiner sind! Gut. Ausgedacht, umgesetzt und getestet.
      Soweit so gut. Bis ich dann auf ein "kleines" Problem gestoßen bin, welchem ich wohl ohne Hilfe nicht gewachsen bin
      schaun mer mal ....


      Originally posted by Trynn View Post
      Nun zum Problem:
      ....
      Löse dich bei Replikationsverfahren im Netzwerkbetrieb von der Bedeutung eines Timestamps. Weiterhin ist nicht jede Zeitinformation auch die die du denkst, zum Beispiel ändern sich manche Konstanten während einer Transaktion nicht, egal wie lange die läuft.

      Originally posted by Trynn View Post
      Firebird unterstützt - meines Wissens - leider den SQL92 Standard nicht vollständig. Genauer gesagt ist es nicht möglich Uncommited-Datensätze einer anderen Transaktion auszulesen. Damit fällt Lösung 2 sowieso flach.
      Warum brauchst nicht committete Datensätze woanders? Diesen Wunsch werde ich niemals verstehen? Nicht committed heisst nicht existent! Dirty read ist eine Krücke.

      Originally posted by Trynn View Post
      Bin für alle Ideen sehr dankbar :-)
      Hoffe dazu fällt jemanden etwas ein, bin mittlerweile etwas ratlos :
      Wenn du etwas loggen willst dann läuft das im Kontext der verursachenden Transaktion. Wenn die committed ist musst du loggen, wenn die rollback ist wird auch nichts geloggt. Macht ja Firebird nun mal automatisch, wenn du das per Triggger machst.

      Deine Beschreibung war mir irgendwie zu theoretisch, in der Praxis habe ich schon diverse Datenbanken mit diversen Konfigurationen hin und her repliziert, (Stern, Netz, online, offline, ...). Letzte Woche war ein Workshop bei uns im Haus, im November auf der Firebird Konferenz halte ich dazu noch weitere Vorträge, ...

      Lösungsansätze kenn ich also reichlich, aber ich müsste erst mal die Problematik verstehen.

      Comment


      • #4
        Hallo,
        schonmal danke für eure Mühen zumindest mal die Problematik zu verstehen
        Ich muss gestehen es fällt mir irgendwie schwer es logisch verständlich zu formulieren ;(
        Zuletzt editiert von Trynn; 09.11.2009, 22:55.

        Comment


        • #5
          Hallo,

          du brauchst auf jeden Fall einen anderen Mechanismus um zu erkennen, ob sich Daten geändert haben. In der Regel mit einer zentralen Log-Tabelle, in die die Änderungen mitprotokoliert werden.

          Handelt es sich um eine unidirektionale Sache (Server-DB => Client-DB), dann brauchst du diese Log-Tabelle nur in der Server-DB. Wenn auch Änderungen am Client gemacht werden sollen und diese in die Server-DB zurückgeschrieben sollen müssen, dann brauchst das auch in der Client-DB. Bei bidirektional kommst du dann automatisch in mögliche Konfliktszenarien rein, die per vordefinierten Regeln und/oder Benutzerinteraktion aufgelöst werden müssen, Vermeidung von Zyklen, ...

          Im Wesentlichen muss der Client wissen, ab welchem Eintrag in der Server-DB Log-Tabelle, dieser für die Datenübertragung "aufsetzen", d.h. losstarten muss. In der Server-DB Log-Tabelle gibt es einen BIGINT Identifier, den sich der Client nach der erfolgreichen Übertragung dann merkt. Kommen nun neue Änderungen in der Server-DB hinzu, dann wird der Identifier in der Log-Tabelle durch die neuen Einträgen automatisch hochgezählt. Das nächste mal, wenn sich der Client mit der Server-DB connected, holt sich der Client einfach alle geloggten Einträge die > seinem lokalen Identifier der Log-Tabelle sind und macht einen Abgleich mit dem lokalen Datenbestand.


          Thomas
          Thomas Steinmaurer

          Firebird Foundation Committee Member
          Upscene Productions - Database Tools for Developers
          Mein Blog

          Comment


          • #6
            Hallo Thomas,
            danke für deinen Beitrag. Sowas in diese Richtung hab ich mir auch schon gedacht. Leider befürchte ich das ich mit einer Tabelle die Datenbank extrem "aufblase"

            Lg, Trynn
            Zuletzt editiert von Trynn; 09.11.2009, 22:55.

            Comment


            • #7
              Hallo,

              ein Generator wird dir in diesem Fall auch nicht helfen, weil dieser außerhalb einer Transaktionssteuerung operiert, d.h. die Reihenfolge nicht der Commits entspricht und bei einem Rollback auch nicht zurückgesetzt wird, d.h. es können Lücken entstehen.

              Bei der Verwendung von Events wäre die Commitreihenfolge sichergestellt, da ein Client von einem Event erst beim Commit benachrichtigt wird.

              Was es seit Firebird 2.1 noch geben würde ist ein Datenbank Trigger (= nicht an eine Tabelle gebunden), der beim Commit feuert. Allerdings befürchte ich, dass dir in so einem Trigger zu wenig "Kontextinformation" zur Verfügung steht, um z.B. auf die Tabelle, usw... schließen zu können. Jetzt könnte man noch meinen, na dann Join ich mir über die CURRENT_TRANSACTION noch die MON$ Tabellen hinzu. Meiner Meinung, auch nicht wirklich eine Lösung, wenn man etwas Last erwartet und die Vergangenheit gezeigt hat, dass der Aufbau der MON$ Tabellen bei Last auch ev. seine Probleme macht.

              Thomas
              Thomas Steinmaurer

              Firebird Foundation Committee Member
              Upscene Productions - Database Tools for Developers
              Mein Blog

              Comment


              • #8
                Hm,
                Bei der Verwendung von Events wäre die Commitreihenfolge sichergestellt, da ein Client von einem Event erst beim Commit benachrichtigt wird.
                Damit würde ich sicherstellen das ich NACH dem commit meine SQLs ausführe.
                Aber ein Event liefert mir doch auch keinen Kontext zum Datensatz, oder gibt es da Möglichkeiten?

                Lg,
                Trynn
                Zuletzt editiert von Trynn; 09.11.2009, 22:56.

                Comment


                • #9
                  Hallo,

                  nun ja, der Ansatz von Thomas heisst ja nicht, dass du diese Änderungen ewig in der Tabelle behälst.
                  Du könntest sie z.B. nach 2 Tagen wieder löschen.

                  Noch ein anderer Quick & Dirty Ansatz.
                  Ich gehe mal davon aus, dass zwischen dem Update und Commit nicht Tage liegen
                  Lese doch einfach alle Änderungen aus, die X (letzte Update-Zeit des Clients)-10 min
                  zurückliegen.
                  Ich weiss, ist Quick & Dirty ...


                  Heiko

                  Comment


                  • #10
                    Hallo Heiko,
                    du wirst lachen aber das hab ich vorrübergehend sogar mal drin gehabt (;
                    Aber Lösung ist das für mich keine, zumal dadurch im Prinzip alle Änderungen mehrfach übertragen werden.
                    Aber du hast schon recht, kleine "Lücken" kann man dadurch umgehen.
                    Zuletzt editiert von Trynn; 09.11.2009, 22:56.

                    Comment


                    • #11
                      Hallo,

                      das AFTER hat nichts mit COMMIT zu tun, d.h. ein AFTER Trigger feuert nicht bei einem Commit.

                      Thomas
                      Thomas Steinmaurer

                      Firebird Foundation Committee Member
                      Upscene Productions - Database Tools for Developers
                      Mein Blog

                      Comment


                      • #12
                        Hallo,
                        leider etwas verzögert, konnte ich den Lösungsansatz mit Logtabelle + After-Update Trigger testen.

                        das AFTER hat nichts mit COMMIT zu tun, d.h. ein AFTER Trigger feuert nicht bei einem Commit
                        Das scheint aber sehr wohl was damit zutun zu haben. Zumindest in meiner Testapplication wird jetzt mit "NOW" im Trigger der Commit-Zeitpunkt geschrieben, egal wieviel Zeit zwischen dem Ausführen des Update-SQLs und dem Commit liegt.

                        Somit ist das die Lösung für die Problemstellung (;
                        Danke an alle beteiligten für die Hilfestellung.

                        Comment

                        Working...
                        X