14 votes

Stratégie de clé primaire pour Android (application distribuée)

Je vais mettre en œuvre une application distribuée avec plusieurs clients mobiles et une application serveur basée sur le Web. Ainsi, chaque client et le serveur sont autorisés à générer des entrées de table. J'ai donc besoin de des clés primaires uniques pour tous les participants ET je veux être capable de générer des clés hors ligne .

Quelle est la meilleure approche pour générer des clés primaires que vous utilisez sur des environnements distribués ? Pour une question similaire, voir Quelle est la meilleure stratégie de clé primaire pour une application mobile multi-clients en ligne/hors ligne avec une base de données SQLite et Azure SQL comme magasin central ?

Je suis conscient que Génération de clés UUID est une bonne approche pour ce scénario, mais je veux m'en tenir à une clé avec le nom _id et le type long comme le suggère la plateforme Android.

Je ne veux pas avoir un identifiant composite avec l'identifiant du dispositif (le serveur est aussi un dispositif) et l'identifiant local. Cette approche ne fonctionnerait pas bien de toute façon puisque le serveur devrait être capable de générer des entrées pour un certain client. Dans ce cas, je devrais utiliser l'identifiant du dispositif également sur le serveur.

Par conséquent, ma préférence actuelle est de construire ma clé avec le type de données long (j'ai déjà fait cela dans un autre projet). Je pense que je vais utiliser le haut/bas (voir par exemple ici Quel est l'algorithme Hi/Lo ? ) et ont une clé qui se compose de :

  • identifiant du client (par exemple, ~28 bits) généré par le serveur
  • faible valeur (par exemple ~ 4 bits) incrémentée sur le client, jamais conservée.
  • valeur élevée (par exemple ~ 32 bits) incrémentée sur le client, persistée sur le client seulement

L'identifiant du client doit être récupéré sur le serveur au premier démarrage de l'application mobile. Le premier démarrage nécessite donc une connexion réseau. Cela peut être un inconvénient de cette approche. En ayant l'identifiant du client sur l'appareil, je peux générer des clés sans connexion réseau.

Normalement, l'identifiant élevé est une valeur unique dans la base de données. Lorsqu'un utilisateur désinstalle l'application et la réinstalle, je dois le traiter comme un nouveau client et lui donner un nouvel identifiant. Sinon, je devrais sauvegarder l'identifiant élevé actuel sur le serveur pour pouvoir le restaurer en cas de perte ou de réinstallation - cela n'en vaut pas la peine.

Quelle est la meilleure approche pour obtenir le high id sur Android ? Une clé autoincrémentée n'est pas une solution. J'ai besoin de quelque chose comme une fonction de générateur. Et elle doit être exécutée dans sa propre transaction (pas dans la transaction "utilisateur"). Quelqu'un a-t-il une expérience de cette approche sur Android et peut-il m'indiquer la bonne direction ? (J'ai seulement trouvé ceci réponse ).

Quelle stratégie clé utilisez-vous pour votre application multi-clients (en ligne et hors ligne) ?

2voto

Tom Points 4834

C'est plus des questions que des réponses...

Cela rend les choses plus faciles si vous pouvez générer automatiquement tous vos identifiants, de sorte que vous n'ayez pas à aller les chercher sur le serveur et à vous préoccuper de savoir si vous avez une connexion. Vous mentionnez que vous ne pouvez pas adopter l'approche commune (UUID ou ANDROID_ID) parce que vous allez utiliser un long "comme suggéré par la plate-forme Android".

Faites-vous référence au fait qu'Android suppose que vos tables SQLite auront une clé primaire _id longue ?

Utilisez-vous un datastore ou une base de données SQL sur votre serveur ?

Si vous utilisez un datastore avec des clés hiérarchiques (par exemple, google datastore), pourquoi ne pas utiliser UUID/ANDROID_ID comme identifiant du client, puis un long comme identifiant de l'élément de données. Ensuite, sur le client, vous stockez simplement le long, et sur le serveur, vos entités sont stockées avec un chemin de clé de UUID/long.

Pourquoi écrivez-vous que "l'identifiant élevé doit être une valeur unique dans la base de données" ? Comme il est précédé de l'identifiant du client, peut-être voulez-vous dire qu'il doit être unique sur la base de données locale ?

Pour résoudre le problème de la désinstallation et de la réinstallation de l'application par l'utilisateur, pourquoi ne pas poursuivre votre idée de "sauvegarder l'identifiant élevé actuel sur le serveur pour pouvoir le restaurer en cas de perte ou de réinstallation". Puisque vous prévoyez déjà de récupérer l'identifiant du client lors de la première exécution (et que vous ne pouvez pas attribuer d'identifiants tant que vous ne l'avez pas), vous pourriez également demander au serveur le prochain identifiant élevé disponible.

Vos entités disposent-elles d'un autre matériau clé qui vous permettrait de générer un hachage 32 bits à partir de ce matériau pour votre identifiant élevé ? En supposant que l'identifiant élevé ne doit être unique que sur un client particulier (et en supposant que vous n'aurez pas un nombre massif d'entités sur un client), je pense que vous n'obtiendrez jamais de collision si vous avez un matériau clé décent et que vous utilisez une fonction de hachage qui minimise les collisions.

2voto

user3603546 Points 325

D'après mon expérience : utiliser des identifiants locaux sur l'appareil et des identifiants distincts sur le serveur . Chaque fois que vous communiquez des données sur le fil, convertissez-les de l'un à l'autre. Cela permet de clarifier le processus et de faciliter le débogage. Les routines de conversion restent petites, sont bien isolées et représentent un élément naturel de l'architecture de l'application. Les données qui transitent sur le fil sont censées être relativement petites, de toute façon, et la conversion d'ID ne représentera pas une surcharge importante. De plus, la quantité de données conservées sur le dispositif mobile est vraisemblablement faible (l'essentiel se trouve sur le serveur).

Je propose de faire la conversion sur l'appareil avec un simple tableau ID_local <-> ID du serveur . Le serveur ne devrait fournir qu'une seule procédure : générer un lot de clés, disons 444 nouvelles clés, que, vraisemblablement, le dispositif mobile attribuera ensuite à ses identifiants locaux et enverra les données au serveur avec les identifiants_serveur uniquement. La table de conversion peut être purgée de temps en temps des ID inutilisés, et les ID locaux peuvent être réutilisés, des entiers de 32 bits suffiront certainement.

Motivation

Les tableaux restent petits, la mise en œuvre reste optimale par rapport à l'architecture native du dispositif, isolée des changements architecturaux imprévisibles ailleurs et il y a un point agréable pour le débogage et le traçage, par lequel toutes les données passent.

J'avais une application qui régénérait tous les identifiants à chaque sauvegarde et chargement de fichier de données. C'était étonnamment simple, rapide et ouvrait d'élégantes autres possibilités comme la défragmentation et la consolidation de l'espace ID.

Dans votre cas, vous pouvez changer la technologie du serveur avec des modifications minimes de l'application client. Puisque le client peut de toute façon fonctionner hors ligne, il pourrait utiliser uniquement les identifiants locaux dans la plupart des fonctions. Seul le module de synchronisation irait chercher et convertir les identifiants du serveur.

2voto

ChrLipp Points 6445

J'ai offert deux primes sur cette question et je n'ai pas trouvé la réponse que je cherche. Mais j'ai passé un certain temps à réfléchir à la meilleure solution et peut-être la question n'était-elle pas assez ouverte et trop axée sur la solution que j'avais en tête.

Cependant, il existe de nombreuses stratégies différentes. Aujourd'hui (après la deuxième prime), je pense que la première question à laquelle il faut répondre est la suivante : quel(s) modèle(s) de données avez-vous dans votre environnement distribué ? Vous pouvez avoir

  1. le même modèle de données (ou un sous-ensemble) sur le client et le serveur
  2. modèle de données client et modèle de données serveur différents

Si vous répondez par 1), vous pouvez choisir votre stratégie clé parmi les suivantes

  • en utilisant le GUID
  • en utilisant mon approche High/Low
  • clés de mappage comme l'a suggéré @user3603546

Si vous répondez par 2), il ne me vient à l'esprit que ce qui suit

  • identifiant composite

Je n'ai jamais aimé les identifiants composites, mais quand j'y pense (et que je n'appelle pas cela des identifiants composites de toute façon), cela pourrait être une solution possible. A la suite, je veux esquisser cette solution :

Glossaire :

  • <client key> ... clé primaire générée côté client, le client choisit donc l'implémentation (long _id pour Android)
  • <server key> ... clé primaire générée côté serveur, donc le serveur choisit l'implémentation
  • <client id> ... ID pour l'identification du client
  • <identification du dispositif> ... ID pour identifier le dispositif, il existe une relation 1-n entre le client et le dispositif.

Solution :

  • Ne l'utilisez que si vous avez un modèle de données client et un modèle de données serveur.
  • Le modèle de données du client comporte les champs
    • <client key> clé primaire
    • <clé du serveur> champ de données nullable
  • Le modèle de données du serveur comporte les champs
    • <clé du serveur> comme clé primaire
    • <client key> champ de données nullable
    • <client id> comme champ de données obligatoire pour distinguer le client
  • Lors de la synchronisation du serveur au client, générer la <client key> manquante sur le client et marquer l'entrée comme dirty (de sorte que l'id du client arrive au serveur à la fin de la journée).
  • Lors d'une synchronisation du client au serveur, générez la <clé du serveur> manquante sur le serveur avant de l'enregistrer.
  • Le mappage entre le modèle de données du client et celui du serveur peut être géré par des cadres spécialisés tels que bouteur o Orika Cependant, la génération de la clé doit être intégrée lors de la mise en correspondance.

Je n'ai jamais aimé cette solution car j'ai toujours pensé en termes de modèle de données de serveur. J'ai des entités qui vivent uniquement sur le serveur et j'ai toujours voulu créer ces entités sur le client, ce qui ne serait pas possible. Mais lorsque je pense en termes de modèle de données client, je peux avoir une entité, par exemple un produit, qui donne lieu à deux entités (Produit et ClientProduct) sur le serveur.

1voto

Osmium USA Points 836

Voyons si j'ai bien compris : vous avez besoin d'un numéro de 32 bits qui est unique à l'appareil ? Ok :

  1. Créez le nombre soit de manière aléatoire, soit en hachant le nanotime actuel. Vous obtiendrez ainsi une chaîne assez unique.
  2. Demandez ensuite au serveur si ce numéro a déjà été utilisé. Si c'est le cas, générez à nouveau le numéro et demandez à nouveau.

Si vous hachurez le nano-temps, il est pratiquement impossible (pas totalement impossible, la résistance aux collisions n'est pas à l'épreuve des collisions) d'obtenir le même nombre. Compte tenu du reste de votre chaîne, cela la rendrait totalement unique. Cette méthode ne nécessite pas d'interactions avec le serveur tant qu'elle n'a pas réellement besoin d'utiliser le serveur. Supposons que le client ne soit pas connecté au premier démarrage : générez le numéro, enregistrez-le, et lorsqu'il se connecte, avant toute autre chose, vérifiez si le périphérique existe. Si c'est le cas, recommencez à zéro. De cette façon, vous pouvez obtenir un identifiant de périphérique vraiment unique.

1voto

Doug Simonton Points 926

Il n'y a aucun moyen de savoir avec certitude que les clés que vous générez sur le client sont uniques sur la BD du serveur tant que vous ne communiquez pas avec le serveur.

Si vous communiquez d'emblée avec le serveur, avant de créer des enregistrements du côté client, vous pouvez réserver une série de clés sur le serveur. Par exemple, le serveur peut distribuer des clés par lots de 10 000. Le client communique avec le serveur et réserve le début du lot suivant de clés disponibles, disons 60 000. Le client est alors libre de créer des enregistrements avec des identifiants compris entre 60 000 et 69 999. Lorsque le client n'a plus de clés, il doit demander une nouvelle série de clés. Si tous les clients et le serveur se réservent des clés de cette manière, tous les identifiants générés seront uniques dans la base de données du serveur.

Si vous créez des enregistrements du côté client avant de communiquer avec le serveur, vous devrez corriger ces identifiants une fois que vous aurez reçu une plage réservée du serveur, afin qu'ils soient dans cette plage, avant de synchroniser ces enregistrements avec le serveur.

Je ne sais pas pourquoi vous essayez également d'inclure un identifiant client dans la clé ; le serveur attribue la valeur élevée, et cela suffit pour obtenir des clés uniques générées sur le client.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X