ubuntuusers.de

Datenbanken synchronisieren, inkl. offline-Zeiten

Status: Ungelöst | Ubuntu-Version: Server 14.04 (Trusty Tahr)
Antworten |

ChickenLipsRfun2eat Team-Icon

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Hallo ihr Lieben!

Vorab-Blala (optional:)

Ich nutze auf meinen Mobilgeräten (Handy, Netbook, Schleppi) einen kleinen selbstgebastelten Counter (zählt Kippen und Kaffee 😀), welcher seine Infos in einer SQLite-DB mit zwei Tabellen lokal speichert. Das Ganze würde ich nun gerne nach dem heimkommen im lokalen Netz mit dem Homeserver synchronisieren.

Warum mit dem Homeserver? Nun, ich muss dann nicht immer alle Geräte anschalten, bzw. die synchronisieren sich dann eh alle von alleine, wenn ich im heimischen (W)LAN bin.

Da das eine bidirektionale Synchronisierung ist, scheint mir das etwas schwieriger. Bei der Suche im Netz bin ich schon auf viele Dinge gestoßen, leider hauptsächlich fertige google-APIs, etc. für Android-Bastler. Das mag funktionieren, ich will es aber selbst basteln, da es an sich zu Lernzwecken dient. Ziel des Ganzen ist es, diese Mini-Anwendung als Plasmoid(plasma-widget), terminal-App mit DBUS und Sailfish-App zu schreiben, einfach damit ich ein wenig C++/QML/Qt/MySQL/SQLite/Python... lerne.

Zum Thema

Ich brauche ein wenig Hilfe bei der Theorie zu o.g. "Problem".

Mein momentaner Plan sieht so aus:

  • Datenbank-Bibliothek, die auf allen Systemen eingesetzt werden kann.

  • Pro System/Anwendung eine eigene Oberfläche, die lediglich den Aufsatz auf die Datenbank-Bib baut.

Die Datenbank-Bibliothek muss also zunächst alles brav lokal speichern, was an sich kein Problem darstellt. Zusätzlich möchte ich eine Synchronisierungsfunktion basteln, die bei Bedarf (oder per Dispatcher ausgelöst) die offline- und online-Datenbank abgleicht.

Die Theorie dazu sieht derzeit so aus:

  • Auf dem mobil-Gerät:

    • Timestamp der letzten Synchronisierung in der lokalen DB/config file abspeichern

    • Alle Einträge seit der letzten Synchronisierung auslesen und in einer SQLdump oder einem eigenen Format speichern.

    • Die Datei zum Server übertragen

  • Auf dem Server:

    • Die empfangene Datei prüfen (passiert zwar nix, gehört aber dazu ☺)

    • Die eigenen Einträge seit dem ersten timestamp ebenfalls in ein von mir weiterzuverarbeitendes Format exportieren

    • Diese Datei an das mobil-Gerät senden.

    • Empfangene Daten in die "online-DB" eintragen.

Die Theorie hat aber nen mega Haken. Wenn ich einen Eintrag lokal ändere, der zeitlich VOR dem sync-timestamp liegt, bleibt diese Änderung nur lokal. Jedesmal die ganze Datenbank zu übertragen mag zwar in meinem Fall keine Probleme verursachen, da sie seeehr klein ist, aber es geht ja eher ums Verständnis und "mal gemacht haben". Die könnte auch ein GiB groß sein und über mobile Daten laufen...

Daher bitte ich um Vorschläge, wie man sowas sinnvoller angehen kann, ohne zuviel von der Umsetzung zu verraten ☺

Eine weitere Idee wäre lokal eine "Puffertabelle" zu verwenden, in der neue Einträge und Änderungen an alten gespeichert werden. Diese würde dann zum Server und anschliessend in die eigentliche Tabelle übertragen werden. Das wäre allerdings nen immenser Aufwand. Da wäre es - zumindest bei meiner kleinen Anforderung - wohl sinnvoller, ich würde eine bool-Spalte in die Tabelle bringen, die die Info enthält, ob der Eintrag synchronisiert werden muss oder nicht.

Momentan steh ich da wie der Ochse vorm Berg und bitte um euer Gehirnschmalz ☺

Danke!

/EDIT: Links für MySQL

senden9

Avatar von senden9

Anmeldungsdatum:
8. Februar 2010

Beiträge: 965

Wohnort: Österreich

Was ich bezüglich Theorie noch einwerfen kann wäre der version vector. Die Liste „Other Mechanisms“ dort könnte dort auch noch Denkanstöße liefern.

Offtopic: Ich glaub ich werde mir jetzt auch so etwas bauen. Allerdings mit Webinterface, REST-API und nur für Kaffee.

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Ein (üblicher) Ansatz in der DB wäre, in jeder Änderung auch den Timestamp der Änderung mit zu speichern/loggen.

Dann hast Du einen sicheren und sauberen Anker, was seit der letzten Synchronisierung geändert wurde.
(d.h. auch konkret, dass Du deinen Lokal-Dump anhand dieser Zeitstempel ziehst !)

Zu dem DB-Handwerk habe ich zu wenig Ahnung, da muss wer anders 'ran.

LG,

track

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

senden9 schrieb:

Was ich bezüglich Theorie noch einwerfen kann wäre der version vector. Die Liste „Other Mechanisms“ könnte dort auch noch Denkanstöße liefern.

Würde bedeuten, ich vergleiche zunächst nur die ID und den Versionszähler der gesamten Tabellen von DB1 und DB2 und muss danach die höherwertigen der jeweiligen Datenbank auslesen und in die jeweils andere Datenbank eintragen. Das ist schonmal nen Ansatz. Danke!!!

Offtopic: Ich glaub ich werde mir jetzt auch so etwas bauen. Allerdings mit Webinterface, REST-API und nur für Kaffee.

Mach das. Ist schon erstaunlich, was da im Monat an Geld weggeht... allein für Kaffee. (Den zu hause berechne ich mit 25ct., den gekauften mit 2€).

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

track schrieb:

Ein (üblicher) Ansatz in der DB wäre, in jeder Änderung auch den Timestamp der Änderung mit zu speichern/loggen.

Ja. Wäre ein zweiter Timestamp, der aber mehr Aufwand beim Abfragen bringen würde. Zunächst prüfen, ob es einen "geändert"-Zeitstempel gibt (oder beim Eintragen gleich doppelt anlegen). Da gefällt mir sowas wie der Versionscounter besser, vor allem, weil der immer unter 10 bleiben wird. So oft ändert man ja nix in dem Fall.

Zu dem DB-Handwerk habe ich zu wenig Ahnung, da muss wer anders 'ran.

Das ist nicht so tragisch. Die Abfragen bekomme ich dann schon geschrieben. Wobei gerade MySQL so viele Werkzeuge bereit hält, dass ich da nicht wirklich ne Übersicht habe. Für die kleinen Tabellchen reicht es aber.

Danke dir!

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Noch ein Haken bei der Sache:

MySQL hat bekanntermaßen eine klare und messbare Struktur (int, unsigned int,...), SQLITE3 nicht. Da kann ein Eintrag als tinyint definiert sein aber auch nen blob enthalten. Muss ich das programmiertechnisch ebenfalls berücksichtigen? (in meinem Fall natürlich nicht. Total übertrieben. Aber so liesse sich wohl recht leicht die MySQL-DB "infiltrieren"...

Vllt sollte ich sqlite verdrängen^^

noisefloor Team-Icon

Anmeldungsdatum:
6. Juni 2006

Beiträge: 29567

Hallo,

Da das eine bidirektionale Synchronisierung ist,

Was im allgemeinen als "Master-Master Replikation" bekannt ist.

Wie du schon richtig erkannt hast, ist der trickreiche Part das Konfliktmanagement, was bei einer Master-Master Replikation zwingend ist. Das Konfliktmanagement sollte halt möglichst viel alleine Lösen können - aber auch wissen, wann es den Nutzer / Admin zum Handeln auffordern muss.

Ein DB, die ein ziemlich ausgereiftes Konfliktmanagement hat, weil seit je her auf Master-Master Replikation ausgelegt, ist CouchDB und die API-Kompatible JavaScript Variante PouchDB. Wenn du das einsetzen würdest, brauchst du dir IMHO zumindest um die Replikationen kaum noch Gedanken machen.

Wenn du unbedingt eine SQL-DB einsetzen willst wird's IMHO deutlich komplizierter.

Gruß, noisefloor

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

noisefloor schrieb:

Ein DB, die ein ziemlich ausgereiftes Konfliktmanagement hat, weil seit je her auf Master-Master Replikation ausgelegt, ist CouchDB und die API-Kompatible JavaScript Variante PouchDB. Wenn du das einsetzen würdest, brauchst du dir IMHO zumindest um die Replikationen kaum noch Gedanken machen. Wenn du unbedingt eine SQL-DB einsetzen willst wird's IMHO deutlich komplizierter.

Die CouchDB klingt echt interessant, wobei ich keine Ahnung von JavaScript habe, außer kleinere Teile in QML. Für meine Testanwendung da ist das aber etwas arg übertrieben, da ich sie ja auch auf jedem Klient installieren müsste. Wäre nun nichts schlimmes, aber ich arbeite am liebsten mit dem, was mitgeliefert wird. Daher kam ich auf sqlite3, bzw. MySQL. Ich überlege aber ernsthaft, ob ich meine Master-Master Replikation (danke für die Vokabel!) nicht insofern einfacher gestalte, als dass ich einfach für die paar einzutragenden Teile einen eigenen Datentyp schreibe und ganz auf eine Datenbank verzichte. Das Ganze könnte bspw. ein passenden JSON-Export enthalten, welchen ich dann auch in einer einzelnen CouchDB verwenden könnte.

Die eigentliche Prüfung ist an sich nicht so schwer, da ich an sich nur int und double speichere und Text ausschließlich als Bezeichner verwende. Wenn ich beim Import alle nicht erwünschten Sonderzeichen herausfiltere, die auch beim Erstellen auf dem Endgerät als nicht verwendbar einstellbar sind, sollte es sicher sein.

Zudem ist das eine Übung, um in die Materie reinzukommen. Man wird von so vielen Dingen erschlagen, dass ich kleiner anfangen sollte und mich nicht gleich übernehme ☺

Danke für dein Feedback!

noisefloor Team-Icon

Anmeldungsdatum:
6. Juni 2006

Beiträge: 29567

Hallo,

so eine ähnliche Idee hatte ich auch, als ich nach dem Post noch ein wenig darüber nachgedacht habe. Eigentlich ist die Datenstruktur so simpel, dass jede Art von DB eigentlich übertrieben ist. JSON wäre vielleicht ein gute Alternative.

Gruß, noisefloor

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Hier mal ein Vorschlag, wie die interne Struktur aussehen könnte:

Zwei Datentypen zum Speichern:

  • Die Allgemeine Tabelle

  • Die Datentabelle:

Der Aufbau der allgemeinen Tabelle wäre dann:

ID|strName|strValue

Beispiel:

1|Einheit|Stück
2|Einheit|Liter
3|deleted|deleted
4|item|Zigaretten
5|item|Milchkaffee
6|item|Automatenkaffee

Der Aufbau der Datentabelle:

ID|timestamp|itemNumber|einheitNumber|valueDouble|updateCounter

Beispiel:

1|now|4|1|0.25|0

Würde bedeuten, ich brauche eigene Befehle für

  • insert (neuer Eintrag),

  • update (Bearbeiten eines Eintrages, inklusive hochsetzen des updateCounters),

  • delete (löschen von Doppelbuchungen,etc.),

  • select Auswahl, beziehungsweise Summe der gültigen Einträge im angegebenen Zeitraum, da ich keine Mengen buche, sondern jeder Eintrag eine 1 ist. Optional: Mengenfeld mit Standard 1),

  • inklusive umgesetzte WHERE Klausel, für Einschränkungen nach Zeit, Artikel, Einheit, etc.

Habe ich was vergessen / übersehen?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13205

Nur um mal noch einen anderen Ansatz ins Spiel zu bringen: git hat ja auch ausgeklügelte Methoden, Änderungen zu verfolgen und Konflikte bei parallelen Updates an mehreren Orten zu mergen. Ich weiß jetzt nicht, was Deine Datenstrukturen sind, aber nehmen wir mal an, Du kannst alles in einzelnen JSON-Dokumenten speichern und es sind nicht so viele, dass das Dateisystem wegen zu vielen Dateien in einem Verzeichnis Ärger macht. Dann könntest Du alle diese Dokumente in ein Verzeichnis packen und mit git verwalten. Dann musst Du "nur" noch einen Weg finden, die Konfliktauflösung zu realisieren. Da das in Deinem Fall Zähler sind, müsste ein Dreiwege-Merge so aussehen, dass Du den Zählerstand auf z0 + (z1 - z0) + (z2 - z0) = z1 + z2 - z0 setzt. git würde Dir dann die Dateien mit Konflikten liefern und Du müsstest die manuell verarbeiten.

noisefloor schrieb:

Ein DB, die ein ziemlich ausgereiftes Konfliktmanagement hat, weil seit je her auf Master-Master Replikation ausgelegt, ist CouchDB und die API-Kompatible JavaScript Variante PouchDB. Wenn du das einsetzen würdest, brauchst du dir IMHO zumindest um die Replikationen kaum noch Gedanken machen.

Ist das wirklich so? Wenn ich mich recht erinnere, dann speichert CouchDB JSON-Dokumente. Replikation funktioniert so lange automatisch, wie es keine Konflikte gibt. Wenn ein Konflikt auftritt, erkennt das die DB natürlich. Aber in dem Fall muss man sich beide (oder sogar mehrere) Versionen eines Dokuments von der DB geben lassen, sie "irgendwie" mergen und dann festlegen, was der aktuelle Stand sein soll.

Wenn du unbedingt eine SQL-DB einsetzen willst wird's IMHO deutlich komplizierter.

Jau. Es gibt da etwas für PostgreSQL, aber das ist noch kein Serienfeature. Und auch da muss man die Konfliktauflösung selbst machen, weil das einfach kein automatisches System entscheiden kann. Dazu braucht es Wissen über die Natur der Daten und deren Verwendung.

noisefloor Team-Icon

Anmeldungsdatum:
6. Juni 2006

Beiträge: 29567

Hallo,

Ist das wirklich so? Wenn ich mich recht erinnere, dann speichert CouchDB JSON-Dokumente. Replikation funktioniert so lange automatisch, wie es keine Konflikte gibt.

Korrekt.

Wenn ein Konflikt auftritt, erkennt das die DB natürlich.

Korrekt.

Aber in dem Fall muss man sich beide (oder sogar mehrere) Versionen eines Dokuments von der DB geben lassen, sie "irgendwie" mergen und dann festlegen, was der aktuelle Stand sein soll.

Auch richtig. Wie ich ja auch schrieb: es gibt kein Konfliktmanagement, was garantiert ohne Nutzer-Interaktion auskommt. Wenn du Dokument im FooBarverschiedenen Änderungen auf verschiedenen Rechner machst, dann musst du ggf. beim nächsten Sync manuell eingreifen. Das ist bei Git oder allen anderen DVCS ja auch so.

Gruß, noisefloor

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Mein Konfliktmanagement wäre ja bei doppelter ID auf die updateCounter zurückzugreifen und den höheren Wert als gültig zu nehmen und damit den Eintrag abzuändern.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13205

ChickenLipsRfun2eat schrieb:

Mein Konfliktmanagement wäre ja bei doppelter ID auf die updateCounter zurückzugreifen und den höheren Wert als gültig zu nehmen und damit den Eintrag abzuändern.

Das würde aber nicht die korrekte Zählung wiedergeben, oder? (siehe meine Berechnung weiter oben)

ChickenLipsRfun2eat Team-Icon

(Themenstarter)

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

rklm schrieb:

Das würde aber nicht die korrekte Zählung wiedergeben, oder? (siehe meine Berechnung weiter oben)

Meinst du die Zählung der Änderungen? Ein "Menge"-Feld gibt es ja so gesehen nicht, weil jede Buchung die Menge 1 darstellt.

Beispiel:

  • Rechner1 bucht. Zähler 0.

  • Rechner2 übernimmt, da die ID fehlt. Zähler noch 0.

  • Rechner3 übernimmt, ändert, dadurch Zähler 1.

  • Rechner1&2 übernimmt, Zähler 1.

  • usw.

Falls ich auf zwei Rechnern den selben Wert auf verschiedene Daten ändere, wäre das aber ein Konflikt, der nicht abgedeckt würde, da durch den dann gleichen Zähler gnadenlos das zuerst synchronisierte Gerät den Zähler auf den Änderungswert setzt und der dritte Rechner mit der zweiten Änderung so tut, als wäre es schon abgeglichen.

Dann wäre der Eintrag eines Zeitstempels ratsam, wie track vorschlug. Da es sich um keine Mehrbenutzer-Umgebung handelt, sondern nur einen Benutzer, der halt je ein anderes Gerät rumschleppt, wäre das wohl in meinem Fall vernachlässigbar, aber die sicherere Lösung. Aktuellster Änderungsstempel hätte dann jeweils Vorrang.

Antworten |