Einzigartigkeit von Objekten in Server - Client sicherstellen



  • Hallo zusammen,

    aktuell habe ich ein Programm mit ner lokalen Datenbank. Mein Domain Objekt bekommt von der Datenbank eine auto increment id. Diese wird auch nicht im Domain Model selbst gespeichert aktuell.

    Nun möchte ich das ganze um einen Server erweitern mit mehreren Nutzern. Und die Frage ist jetzt wie ich sicherstelle, dass meine Objekte einzigartig sind.

    Der Client muss sich nicht mit dem Server verbinden bzw. kann auch offline arbeiten. Er muss also neue Objekte anlegen können ohne das er in diesem Moment mit dem Server verbunden ist.
    Es gibt außerdem auch keinen natural key. Es muss also irgendeine Form von ID geben, um die Objekte voneinander zu unterscheiden.

    Meine Idee:

    Eine GUID verwenden. Das Objekt bekommt ein ID Feld und die Applikation erzeugt bei Erstellung eines Objektes diese GUID.

    Überlegungen / Unklarheiten:

    • Gehört eine ID überhaupt in das Domain Object? (In diesem Fall würde ich sagen ja, denn sie gewährt ja die Einzigartigkeit und ist es jetzt nicht unbedingt ein Implementationsdetail der Datenbank)
    • Ist die GUID dann auch der primary key? (Damit wären ja z.B. joins etc. relativ kostenaufwendig, da der so lange ist)
    • Ist der dann auch Teil der REST Api bei nem Get Request also z.B. mydomain/myobject/4787dacb-a483-4b0e-bfc3-e5e996322183 -> Ist ja nicht super schön. Und so lange identifier habe ich tatsächlich bei bekannten Applikationen auch noch nicht so gesehen. Ne numerische ID, etwas kürzer, dagegen schon .... -> Macht man das mit der GUID also wirklich so?
    • Muss es wirklich ne GUID sein? Könnte es nicht auch einfach die systemzeit in Millisekunden + nutzer_id oder so sein? Wäre doch eig. auch einzigartig? Das ein Nutzer auf 2 Geräten gleichzeitig ein Objekt erstellt kommt mir unrealistisch vor.


  • @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Gehört eine ID überhaupt in das Domain Object? (In diesem Fall würde ich sagen ja, denn sie gewährt ja die Einzigartigkeit und ist es jetzt nicht unbedingt ein Implementationsdetail der Datenbank)

    Warum stellt sich die Frage jetzt mit der Erweiterung? Warum waren die Objekte vorher einzigartig, wenn der Schlüssel doch nur in der DB benutzt wird?



  • @manni66 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Gehört eine ID überhaupt in das Domain Object? (In diesem Fall würde ich sagen ja, denn sie gewährt ja die Einzigartigkeit und ist es jetzt nicht unbedingt ein Implementationsdetail der Datenbank)

    Warum stellt sich die Frage jetzt mit der Erweiterung? Warum waren die Objekte vorher einzigartig, wenn der Schlüssel doch nur in der DB benutzt wird?

    Das liegt primär daran, dass es vorher einen natural key gab, den es jetzt nicht mehr geben soll. (Das war mir ehrlicherweise selbst grade noch gar nicht so bewusst. Dachte das hätte ich schon umgebaut).

    Ein weiterer Grund ist aber auch, dass diese Einzigartigkeit bisher auch nur wirklich in der Datenbank selbst relevant war. Und ich die eben auch anders gewährleisten konnte. In dem Fall habe ich das so gelöst, dass ich in meiner Datenbank Schicht einen "Cache" std::pair<Id, Objekt> hatte. Wenn ein Objekt zur Änderung rausgegeben wurde, habe ich nur ne Referenz des Objektes rausgegeben. Wurde dann irgendwann update() aufgerufen, habe ich das gecache Objekt über die mitgespeicherte ID wieder in die Datenbank geschoben. So musste ich die ID nicht mit raus geben.

    Mit dem Server wird die Identifizierung / Eindeutigkeit wichtiger. Sie definiert die Endpunkte, damit wird gesynct etc. Es scheint mir nicht mehr nur was Datenbank internes zu sein. Ich wüsste auch gar nicht wie es anders gehen soll als die ID im Objekt zu haben (Mit Ausnahme davon vlt. statt nur dem Objekt immer noch die ID durch alle Methoden durchzureichen, das ist aber ja im wesentlichen das selbe).



  • @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Ist die GUID dann auch der primary key? (Damit wären ja z.B. joins etc. relativ kostenaufwendig, da der so lange ist)

    Eine UUID ist eine 128 Bit Zahl. Du musst sie in der DB nicht als String speichern.

    Ich benutze in einer Oracle Datenbank RAW Werte. Das funktioniert gut und ist nicht langsam.

    Ist der dann auch Teil der REST Api bei nem Get Request also z.B. mydomain/myobject/4787dacb-a483-4b0e-bfc3-e5e996322183 -> Ist ja nicht super schön. Und so lange identifier habe ich tatsächlich bei bekannten Applikationen auch noch nicht so gesehen. Ne numerische ID, etwas kürzer, dagegen schon .... -> Macht man das mit der GUID also wirklich so?

    Solage man dir URL nicht eintippen muss: warum nicht?

    Muss es wirklich ne GUID sein? Könnte es nicht auch einfach die systemzeit in Millisekunden + nutzer_id oder so sein? Wäre doch eig. auch einzigartig? Das ein Nutzer auf 2 Geräten gleichzeitig ein Objekt erstellt kommt mir unrealistisch vor.

    Das geht auch. Jetzt hast du die Qual der Wahl 😉



  • @manni66 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Eine UUID ist eine 128 Bit Zahl. Du musst sie in der DB nicht als String speichern.

    hmm da ich für den client einen sqlite3 Datenbank nehem, müsste ich es wohl doch als String speichern. Die kann ja nur 64 bit.

    @manni66 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Solage man dir URL nicht eintippen muss: warum nicht?

    Stimmt schon 🙂 Ich hatte mich nur einfach gewundert, ob das üblich ist / etwas dagegen spricht. Solche GUID sind mir bisher selten aufgefallen. Ich hab allerdings mal auf Amazon geschaut, da sehen die URLs auch nicht hübsch aus. Bin mir nicht sicher, ob sie da speziell ne GUID verwenden, aber lange und hässlich ist die URL definitiv.

    @manni66 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Das geht auch. Jetzt hast du die Qual der Wahl

    Ich hatte mir jetzt eher so Vor- und Nachteile erhofft. Oder ein paar nett gemeinte Hinweise, worauf ich da achten muss 😃
    Oder würdest du wirklich sagen, es ist völlig wurscht? Beides gleich gut!

    Mir sind persönlich nur 2 Sachen aufgefallen:

    • In den meisten Quellen, die ich so gefunden habe, ist immer die Rede von GUID. Vorschläge mit Systemzeit o.ä. habe ich bisher noch nicht gefunden.
    • Eine GUID zu erzeugen ist erstmal nicht so einfach. Da gibt es plattformabhängige Libs soweit ich das sehe. Das beste was ich bisher gesehen habe ist "https://github.com/graeme-hill/crossguid", welche die Pattformen wegabstrahiert. Das sehe ich erstmal als einen Nachteil ... extra lib, plattformabhängig (auch wenn hier die gängigsten Plattformen abgedeckt sind)

  • Mod

    Zufallszahlen sind schlecht für gewisse Indextypen. Ein übliches Verfahren ist daher, dass man eine jeweils eine Kennnummer für die verteilten Systeme nimmt, und auf diesen Systemen dann sequentiell zählt. Da kann beim Zusammenführen nicht einmal theoretisch etwas schief gehen, es ist sehr partitionierungs- und indizierungsfreundlich, für Entwickler/Maintainer recht leicht verständlich, und als Bonus kann man einem Datensatz direkt ansehen, woher er kommt. (Theoretischer Nachteil: Es könnten einem irgendwann die Nummern ausgehen, aber ein bisschen Voraussicht bei der Wahl der Nummernblöcke sollten wir annehmen)



  • @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Die kann ja nur 64 bit.

    2*64 = 128

    Eine GUID zu erzeugen ist erstmal nicht so einfach. Da gibt es plattformabhängige Libs soweit ich das sehe. Das beste was ich bisher gesehen habe ist "https://github.com/graeme-hill/crossguid", welche die Pattformen wegabstrahiert. Das sehe ich erstmal als einen Nachteil ... extra lib, plattformabhängig (auch wenn hier die gängigsten Plattformen abgedeckt sind)

    Wenn in den bisher genutzten Bibliotheken nichts drin ist, würde ich mich gegen UUID entscheiden.



  • @SeppJ

    Könntest du nochmal erläuter, was du genau mit "Kennummer" hier meinst? Eine Nummer, die den Computer auf dem die App läuft, identifiziert?

    Oder wie soll das mit dem sequentiell hochzählen bei mehreren clients funktionieren? Die müssen dann ja alle eine eigenen "Nummernblock" haben


  • Mod

    @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Oder wie soll das mit dem sequentiell hochzählen bei mehreren clients funktionieren? Die müssen dann ja alle eine eigenen "Nummernblock" haben

    Ja. Client 1 bekommt 0 bis 1,000,000,000, Client 2 bekommt 1,000,000,000 bis 2,000,000,000, usw. Mit einer Blockgröße, die lächerlich groß ist gegenüber dem was maximal zu erwarten ist (denn wir alle wissen, das die maximalen Erwartungen stets übertroffen werden).



  • Wie wäre es mit IP-Adresse und UNIX Epoch ggf. mit Process ID erweitert?


  • Mod

    @john-0 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Wie wäre es mit IP-Adresse und UNIX Epoch ggf. mit Process ID erweitert?

    Klingt nach einem Rezept für Kollisionen. Für so etwas willst du entweder systematisch ausschließen, dass es jemals Kollisionen gibt (z.B. mein Vorschlag), oder wenn es vom Zufall abhängt, dann sollte es doch so unwahrscheinlich sein, dass eher das Universum ausbrennt als das man kollidiert (GUID).

    Process IDs gibts meist nur ein paar Zehntausend, Zeit ist für alle auf der Welt gleich und eine Sekunde ist ganz schön lang. IPs könnten aus einem LAN kommen, wo eine Handvoll IPs sehr häufig sind, oder es könnte die öffentliche IP von einem großen Provider sein, der intern NATed. Dann hast du mit einigen Anfragen pro Sekunde dank Geburtstagsparadoxon fast schon garantiert, dass das bald schief geht.



  • Ich würde da ganz einfach sequential GUIDs verwenden: https://docs.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential

    Je nachdem wie die DB das Feld sortiert muss man evtl. die Bits in der generierten GUID ein wenig rumschieben. Davon abgesehen sollte das die Anforderungen erfüllen.

    Ansonsten... wenn die Clients eine eindeutige ID haben, dann kann man natürlich auch "locally unique identifiers" + die Client ID verwenden. In dem Fall kann man aus der zusammengehängten ID halt 1:1 rauslesen welcher Client sie angelegt hat. Was u.U. unerwünscht sein könnte.

    Wobei... aus sequential GUIDs kann man auch so einiges rauslesen. Also die sollte man auch nicht in jedem Fall verwenden. Falls das ein Problem ist könnte allerdings jeder Client beim Installieren eine (non-sequential) GUID erzeugen und irgendwo abspeichern. Ausgehend von dieser kann man dann entweder einfach zählen (dann muss man den Zähler halt selbst managen). Oder man könnte sich dann pro Objekt eine sequential GUID erzeugen lassen, und die finale ID ist dann die Summe der gespeicherten GUID und der sequential GUID. Das clustert dann gleicht gut wie die sequential GUID, aber man kann nicht mehr auf die lokale MAC Adresse o.Ä. rückschliessen.



  • Hallo @Leon0402,

    @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Und die Frage ist jetzt wie ich sicherstelle, dass meine Objekte einzigartig sind

    Da kommt man meines Erachtens an eine Objekt-ID nicht vorbei. Es bietet sich ein eigener Typ an, der z.B. aus drei Werten bestehen könnte, die wären:

    1. Eine DB-ID (falls man Objekte aus mehreren Datenbanken haben möchte).
    2. Eine Class-ID (falls Klassen (Typen) unterschieden werden sollen).
    3. Ein Zähler, der aus der Datenbank ermittelt wird. Aus der DB, weil dann Server wie Client eine eindeutige zahl bekommen.

    @Leon0402 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Der Client muss sich nicht mit dem Server verbinden bzw. kann auch offline arbeiten. Er muss also neue Objekte anlegen können ohne das er in diesem Moment mit dem Server verbunden ist.

    Da sehe ich ein Problem. Wie soll verhindert werden, dass ein Server und ein nicht mit dem Server verbundener Client gleichzeitig schreiben? Von Problematiken mit Locks oder Transaktionen mal abgesehen. Möglich wäre ggf.: Client kann über Server oder direkt zugreifen. Client kann nicht direkt zugreifen wenn schon anderer Client oder Server zugreift. Server kann nur zugreifen wenn kein anderer Client oder Server zugreift.
    Wie löst Du den z.Z. konkurrierende Schreibzugriffe von Clients auf die Datenbank?

    Gruß Helmut



  • @Helmut-Jakoby sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Da sehe ich ein Problem. Wie soll verhindert werden, dass ein Server und ein nicht mit dem Server verbundener Client gleichzeitig schreiben? Von Problematiken mit Locks oder Transaktionen mal abgesehen. Möglich wäre ggf.: Client kann über Server oder direkt zugreifen. Client kann nicht direkt zugreifen wenn schon anderer Client oder Server zugreift. Server kann nur zugreifen wenn kein anderer Client oder Server zugreift.
    Wie löst Du den z.Z. konkurrierende Schreibzugriffe von Clients auf die Datenbank?

    Ich verstehe nich so ganz, was du meinst. Also natürlich hat jeder Client seine eigene Datenbank und es gibt (erstmal) eine zentrale Server Datenbank auf dir nur der Server zugreift.
    Insofern ist das einzige konkurrierende gleichzeitige Anfragen an den Server und gleichzeitiges Schreiben mehrerer Threads des Servers.

    Ist aus meiner Sicht hier ein anderes Problem, was aber die meisten Server Datenbanken auch schon implementieren afaik.

    Vlt. willst du auch auf Synchronisierung hinaus? Also klar wenn Client A in seine lokale Datenbank schreibt und Client B auch in seine lokale Datenbank schreibt und das selbe Objekt anders verändern, dann gibt es ein Konflikt, sobald beide irgendwann zum Server pushen / pullen. Konflikt Behebung sehe ich aber auch hier als ein eigenes Problem. Der einfachste Ansatz ist hier ja: Don't care ... einfach das neuste nehmen. Oder den User fragen willste die Version oder die? etc. ... das gibt es viele recht einfache Ansätze für den Start.



  • Nur so als Idee, was spricht denn dagegen in der SQL-Datenbanktabelle eine Spalte "SYSID" o.ä. zu ergänzen, die quasi den Client angibt, der den Datensatz erfasst hat und eine "ID" Spalte, die die ID aus der Client-DB enthält?

    Damit spart man sich Nummernblöcke. Den entsprechenden Entstehungsort des Datensatzes kriegt man dann über die SYSID raus.



  • @inflames2k Nichts direkt. Das ist ja quasi die Idee von @hustbaer und in gewisser Weise auch die von Nummernblöckern (Nummerblock = client id, statt hochzählen des Nummernblocks wird eine neue hochgezählte Variable hinten dran gehängt)

    Die Nachteile sind halt:

    • Der Client braucht ne unique id und man kann halt von objekten Rückschlüsse auf den Client führen (wobei das in meinem Fall denke ich kein Problem wäre)
    • Man hat mehr Verwaltungsaufwand, weil quasi zwei verschiedene Ids

    Wobei letzeres nur auf deine Idee zutrifft. So wie ich hustbaer verstanden habe, war da die Idee beides direkt in ein Feld zu mergen.

    Bei deiner Idee würden mich dann auch etwas wieder Design Fragen plagen:

    z.B. sollten die IDs Teil des Domain Models sein? Lokal reicht ja die ID zu Identifizierung, die SYSID wäre quasi ein Implementierungsdetail vom Server.



  • @SeppJ sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Klingt nach einem Rezept für Kollisionen. Für so etwas willst du entweder systematisch ausschließen, dass es jemals Kollisionen gibt (z.B. mein Vorschlag), oder wenn es vom Zufall abhängt, dann sollte es doch so unwahrscheinlich sein, dass eher das Universum ausbrennt als das man kollidiert (GUID).

    Das ganze Vorgehen klingt nach einem Rezept für Kollisionen. Egal was für man eine ID zusammenbaut, entscheidend ist, dass die Daten gleich sein können, d.h. man wird unbedingt den Inhalt auf Gleichartigkeit prüfen müssen. Und ich habe meine Zweifel, dass IPv6 Adressen+Zeitstempel wirklich zu einer Kollision führen. Aber man kann natürlich auch die MAC+Zeitstempel nehmen.



  • @john-0 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Aber man kann natürlich auch die MAC+Zeitstempel nehmen.

    Womit man eine Version 1 UUID nachbaut. Dann würde ich gleich zum Original greifen.



  • @john-0 So wie ich das verstanden habe, kann man bei der GUID eigentlich davon ausgehen, dass es keine Kollisionen gibt (weil zu unwahrscheinlich).

    Der unwahrscheinliche Fall, dass es eine Kollision gibt, könnte man vermutlich auch ganz gut abfangen. Die Datenbank sollte ja einen Fehler schmeißen auf Server Seite, wenn eine Objekt mit gleichem Primary Key eingefügt wird. Dann kann der Server darauf reagieren. Müsste man nur überlegen wie man dem Client mitteilt, dass er doch bitte die ID nochmal ändern soll.

    @john-0 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Das ganze Vorgehen klingt nach einem Rezept für Kollisionen.

    Welches ganze Vorgehen? Ein Server - Client Modell mit Offline Funktionalität? Oder die eindeutigen IDs Clientseitig zu generieren? -> Das war ja nicht in den Stein gemeißelt, sondern meine Idee. Ich freue mich auch gerne über Alternative Vorschläge!

    @hustbaer sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Ansonsten... wenn die Clients eine eindeutige ID haben, dann kann man natürlich auch "locally unique identifiers" + die Client ID verwenden. In dem Fall kann man aus der zusammengehängten ID halt 1:1 rauslesen welcher Client sie angelegt hat. Was u.U. unerwünscht sein könnte.

    Die Idee gefällt mir schon auch sehr gut. Ob es schlimm ist, wenn man den Client rauslesen kann? -> Eher nicht. Es sollte eh eine Zuordnung zu den Benutzeraccounts (Was ja fast das selbe wie zum einzelnen Client ist) geben.

    Meine nächste Frage wäre jetzt allerdings. Woher bekommen die Clients ihre eigene ID?

    Was ich an dieser Stelle dazu sagen sollte: Ich möchte eigentlich, dass der Server optional ist. Somit gibt es aus meiner Sicht nur 2 Möglichkeiten:

    1. Der Client generiert sie selbst
    2. Der Client bekommt erst bei (optionaler) Registrierung seine ID

    Ich vermute, dass in beiden Fällen, die ID seperat abgespeichert werden sollte und nicht für jedes Objekt. Das wären sonst ja nur Dopplungen.

    Perspektisch sehe ich hier allerdings ein Problem später, was Synchronisation angeht. Sagen wir Benutzer A hat 2 Clients und legt auf beiden offline ein neues Objekt an. Beide Rezepte haben somit dieselbe ID (Gehe mal davon aus, man nutzt ihr einfach Auto Increment). Jetzt pushen beide zum Server und es gibt natürlich den Konflikt.

    Lösung: Clients pullen erst (sowieso sinnvoll) und der spätere Client stellt dann fest: Oh ein neues Objekt hat die selbe ID wie eins in meiner Datenbank. Aber das in der Datenbank ist nicht auf dem Server bisher, also muss es ein anderes sein. Deswegen verschiebt dann der Client das nicht synchronisierte Objekt in der Datenbank auf eine neue unbesetze Stelle.
    Das klingt potentiell nervig. Insbesondere wenn es den Konflikt direkt bei mehreren Objekten gibt, weil beide Clients länger offline waren.
    Nutzt man jetzt nicht Auto Increment, sondern generiert ne zufällige (aber eher kleine) Zahl ist das Risiko minimiert, aber natürlich nicht weg.


  • Mod

    @john-0 sagte in Einzigartigkeit von Objekten in Server - Client sicherstellen:

    Das ganze Vorgehen klingt nach einem Rezept für Kollisionen. Egal was für man eine ID zusammenbaut, entscheidend ist, dass die Daten gleich sein können, d.h. man wird unbedingt den Inhalt auf Gleichartigkeit prüfen müssen. Und ich habe meine Zweifel, dass IPv6 Adressen+Zeitstempel wirklich zu einer Kollision führen. Aber man kann natürlich auch die MAC+Zeitstempel nehmen.

    Deine Zweifel oder dein Unwissen um die Eigenschaften von MAC-Adressen sind irrelevant.


Anmelden zum Antworten