Synchronisation von einer mySQL mit lokalen SQLite und mehreren Usern.

  • Antworten:7
Jokel
  • Forum-Beiträge: 1.530

16.02.2019, 11:28:38 via Website

Hallo
Mal eine Frage zur Synchronisation von einer mySQL mit lokalen SQLite und mehreren Usern.
Angenommen ich habe eine mySQL auf einem Server.
Über PHP Skripte können User Datensetze Speichern Ändern.
Die User sollen auch Offline Daten ändern können. Um offline Arbeiten zu können wird diemySQL Über Json in eine lokale SQLite kopiert.
Um Änderungen mit zu bekommen habe ich jeden Datensatz einen Timestamp hinzugefügt.
Dieser wird beim wieder Online sein verglichen und an die Server mySQL gesendet. Das PHP Skript fügt die Änderungen ein.
Mit nur einem User ist das auch kein Problem.

Wenn ich nun mehrere User habe die Offline sind und Änderungen machen. Habe ich ein Problem.
Zb. User 1 u, 2 sind offline.
User 1 ändert einen Datensatz, kurz danach ändert User 2 den gleichen Datensatz.
So nun geht User 2 zu erst online und nun wird die DB Synchronisiert da der Zeitstempel der offline SQLite neuer ist als der der Server mySQL wird es geupdate, geändert.

Nun geht User 1 Online sein Zeitstempel ist nun älter als der in der MySQL.
Somit werden seine Änderungen ignoriert.
Ich müsste nun den User Informieren das die Server DB aktueller ist und er entscheiden muss.

Wie würdet ihr das handhaben?

MySQL ist ja nun kein Echtzeit DB die die User über Änderungen Informiert .
Das müsste man ja alles selber machen.

Bedanke mich schon mal für die Antworten.

Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

16.02.2019, 19:21:23 via Website

Hallo Jockel,

ja, das ist das Problem.
Du musst den User entscheiden lassen, was passieren soll. Ähnlich wie GIt das in RemoteRepos macht, wenn sich Dateien im Remote und lokal geändert haben. (hier nur etwas einfacher :))

Ich würde es folgendermaßen implementieren:

  1. User geht online, App synct (egal ob im Hintergrund oder nicht)

2a Falls deine PHP API ein "OK" zurückliefert sind alle Daten konsistent
2b. Wenn nicht, kommt ein definierer "Error" (z.b. per ErrorCode etc.) zurück und der Datensatz, mit dem es in der DB Probleme gibt.
Das kannst du dann den User in irgend einer Form (Notification+ResolveSyncActivity ö.ä. oder Popup etc.) promten lassen. Dieser muss dann entscheiden.
3a. Wenn der User sagt, "online Datensatz behalten", dann syncst du den Datensatz online in deine SqLite
3b. Wenn der User sagt, meinen Datensatz hochladen, wird der Eintrag in der online DB aktualisiert.

Das heißt, da kommt eigentlich nur Logik dazu und du musst nicht alles neu machen.

— geändert am 16.02.2019, 19:22:11

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Jokel
Kommentieren
Jokel
  • Forum-Beiträge: 1.530

16.02.2019, 19:47:20 via Website

@Pascal erstmal Danke.
Ja das leuchtet ein. Aber wie bekommen es die anderen User mit das sich die Server DB geändert hat?

Ich müsste somit immer beim online gehen oder beim Start der App die DB in die lokale kopieren. Das wird wohl auch nicht ganz reichen denn die Server DB könnte ja auch zwischendurch von einem anderen User geändert werden.
Auch wenn ich online bin. Somit müsste ich ja ständig die DB abfragen.

Ich bräuchte so etwas wie eine Benachrichtigung, Listner, an die User die online sind das sich die DB geändert hat.

Wie würdest du das machen.

Hilfreich?
Kommentieren
swa00
  • Forum-Beiträge: 3.704

16.02.2019, 19:51:56 via Website

Und wie wäre es dann mit einer simplen FCM Push ?
Server haste ja schon

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
JokelPascal P.
Kommentieren
Jokel
  • Forum-Beiträge: 1.530

16.02.2019, 20:02:39 via Website

Ja das ist eine Möglichkeit ich hätte aber gern einen listner der die lokale db updatet. Oder gibt es da schon einen muss mir da die Dokumentation zu Fcm genauer anschauen. Welche Möglichkeiten gibt es noch außer FCM ?

Wollte eigentlich kein Google verwenden.

— geändert am 16.02.2019, 20:04:38

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.530

16.02.2019, 20:16:27 via Website

Wenn es keine andere Möglichkeit gibt als eine Push FCM.

Dann könnt ihr mir bestimmt einen Tipp geben wie ich das in PHP mache.

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

16.02.2019, 21:11:54 via Website

Hmm, da gibt es genug Ansätze.
Du baust dir einen FCM Listener, der auf eine DB Update Topic/Action reagiert. Dann können deine Clients die PushMessage dann verarbeiten.
Beachte aber, dass FCM Messages Größenbeschränkt sind (genaue Angabe hab ich gerade nicht im Kopf), d.h. wenn deine DB Einträge diesen Wert übersteigen, kannst du die nicht direkt per FCM übertragen, sondern nur ein Token, mit dem du dir dann beim Server die passenden Daten holen musst.

Meine Implementation sieht in etwa so aus (schon ne weile her, das ich das gemacht hab, versucht so gut wie möglich in OOP und daher keine PHP7.x style :O):

require_once("Database.class.php"); //DB kommunikation
class FirebaseCloudMessagingHandler{
    protected $apiKey;
    protected $ids=array();
    public  $db;

    protected $url=   'https://fcm.googleapis.com/fcm/send';

    public function __construct($apiKey){

        $this->db = new Database(databaseName);
        $this->apiKey = $apiKey;
        $this->ids = $this->db->getFCMRegistrationIds(); //ClientRegistrationIDs lesen
    }



    //Send FCM to All Clients -- $message = MessageObjekt mit verschiedenen Feldern als Json
    public function sendMessage($message){     
        //setup fields + headers
        $fields = array(
                'to'  => $this->ids,
                'data'              => array( "action" => $message ), 
                                           //Action ist dann das Topic, am besten die FCM API Dok anschauen
                );

        $headers = array( 
                    'Authorization: key=' . $this->apiKey,
                    'Content-Type: application/json'
                );

        //lets curl
        // Open connection
        $ch = curl_init();

        // Set the url, number of POST vars, POST data
        curl_setopt( $ch, CURLOPT_URL, $this->url );
        curl_setopt( $ch, CURLOPT_POST, true );
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $fields ) );

        // Execute post
        $result = curl_exec($ch);
        print_r($result);
        // Close connection
        curl_close($ch);
        echo $result ."\n";

    }

    //SendMessage to several IDs
    public function sendMessageToIds($message,$IdsToReceive){

        //setup fields + headers
        $fields = array(
                'registration_ids'  => $IdsToReceive,
                'data'              => array( "action" => $message ),
                );

        $headers = array( 
                    'Authorization: key=' . $this->apiKey,
                    'Content-Type: application/json'
                );

        //lets curl
        // Open connection
        $ch = curl_init();

        // Set the url, number of POST vars, POST data
        curl_setopt( $ch, CURLOPT_URL, $this->url );
        curl_setopt( $ch, CURLOPT_POST, true );
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );

               curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
                 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $fields ) );

        // Execute post
        $result = curl_exec($ch);
        if ($result === FALSE) {

    die('FCM Send Error: ' . curl_error($ch)); //evtl. error handling
             }
        // Close connection
        curl_close($ch);

        echo $result ."\n";

    }

    }

Natürlich brauchst du dann noch ein Skript, das beim AppStart die ClientRegistrationId von FCM an deinen Server überträgt. Und das würde ich fast immer beim Start aufrufen, keine Ahnung wann sich die ID ändern kann, aber hatte es schon, dass die sich einfach geändert hat z.b. nach Software Update

Ohne FCM kannst du nur Pollen, das ist kaum eine Option. Oder du hast den Server komplett in der Hand, dann kannst du auch ne TCP aufmachen, aber auf Dauer bracuht das auch genug Akku...
Und eine direktverbindung zu einer Eventgesteuerten DB würde ich direkt lassen.
Evtl. kann Google Firebase DB ja sowas, aber dann hast du auch wieder Google komponenten drin.
Das ganze kannst du natürlich vermeiden indem du keine Offlinearbeit zulässt und einen SingleUserMode hast, wo immer nur einer Bearbeiten darf. Das schränkt aber ungemein ein...

— geändert am 16.02.2019, 21:15:01

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Jokel
  • Forum-Beiträge: 1.530

16.02.2019, 21:28:58 via Website

Danke Pascal dann wird es wohl wirklich keine andere Lösung geben.
Danke für die Tipps und Code.

Das ich keine großen Daten übertragen kann war mir schon klar.
Muss es ja auch nicht sein es soll ja nur eine Info sein das die DB verändert wurde und der Client sich die neue veränderten Daten holen soll.

Nur das man dazu unbedingt Firebase braucht ist nicht so schön.
Ja was soll's werde mir das nochmal durch den Kopf gehen lassen.

Bis hierhin erstmal vielen Dank.

— geändert am 16.02.2019, 21:33:58

Hilfreich?
Kommentieren