31 votes

Comment mettre en œuvre "auto-incrémentation" sur Google AppEngine

J'ai besoin d'étiqueter quelque chose dans un "fort monotone croissante" de la mode. Soit les Numéros de Facture, numéro de l'étiquette d'expédition ou autres.

  1. Un certain nombre ne DOIT PAS ÊTRE utilisé deux fois
  2. Chaque numéro DOIT ÊTRE utilisé quand exactement tous les nombres plus petits ont été utilisés (pas de trous).

Façon élégante de dire: j'ai besoin de compter 1,2,3,4 ... Le nombre d'Espace disponible sont généralement 100.000 numéros et j'ai besoin peut-être de 1000 par jour.

Je sais que c'est un Problème difficile dans les systèmes distribués et souvent, nous sommes beaucoup mieux avec Guid. Mais dans ce cas, pour des raisons juridiques, j'ai besoin de "traditionnel de numérotation".

Cela peut-il être mis en œuvre sur Google AppEngine (de préférence en Python)?

25voto

Nick Johnson Points 79909

Si vous devez absolument avoir des nombres en augmentation séquentielle sans lacunes, vous devrez utiliser une seule entité, que vous mettez à jour dans une transaction pour «consommer» chaque nouveau numéro. En pratique, vous serez limité à environ 1 à 5 nombres générés par seconde, ce qui semble convenir à vos besoins.

7voto

Martin v. Löwis Points 61768

Si vous laissez tomber l'exigence que les Id doivent être strictement séquentiel, vous pouvez utiliser un hiérarchique schéma d'allocation. L'idée de base/limitation est que les transactions ne doivent pas affecter plusieurs groupes de stockage.

Par exemple, en supposant que vous avez la notion de "utilisateurs", vous pouvez attribuer un groupe de stockage pour chaque utilisateur (création de certains généraux de l'objet par l'utilisateur). Chaque utilisateur dispose d'une liste de réservées Id. Lors de l'attribution d'un IDENTIFIANT d'un utilisateur, choisir un réservé une (dans une transaction). Si aucun Id sont de gauche, de faire une nouvelle transaction en allouant 100 Id (dire) à partir du pool global, puis effectuer une nouvelle transaction pour les ajouter à l'utilisateur et, simultanément, de retirer une. En supposant que chaque utilisateur interagit avec l'application que séquentiellement, il n'y aura pas de simultanéité sur les objets utilisateur.

7voto

mdorseif Points 7473

Le gaetk - Google AppEngine Toolkit est désormais livré avec une fonction de bibliothèque simple pour obtenir un nombre dans une séquence. Il est basé sur l'approche transactionnelle de Nick Johnson et peut être utilisé assez facilement comme base pour l'approche de sharding de Martin von Löwis:

 >>> from gaeth.sequences import * 
>>> init_sequence('invoce_number', start=1, end=0xffffffff)
>>> get_numbers('invoce_number', 2)
[1, 2]
 

La fonctionnalité est essentiellement implémentée comme ceci:

 def _get_numbers_helper(keys, needed):
  results = []

  for key in keys:
    seq = db.get(key)
    start = seq.current or seq.start
    end = seq.end
    avail = end - start
    consumed = needed
    if avail <= needed:
      seq.active = False
      consumed = avail
    seq.current = start + consumed
    seq.put()
    results += range(start, start + consumed)
    needed -= consumed
    if needed == 0:
      return results
  raise RuntimeError('Not enough sequence space to allocate %d numbers.' % needed)

def get_numbers(needed):
  query = gaetkSequence.all(keys_only=True).filter('active = ', True)
  return db.run_in_transaction(_get_numbers_helper, query.fetch(5), needed)
 

5voto

Kevin Cox Points 725

Si vous n'êtes pas trop strict sur le séquentiel, vous pouvez "shard" votre incrementer. Cela pourrait être pensé comme un "finalement séquentielle" comptoir.

Fondamentalement, vous avez une entité qui est le "maître" qui comptent. Ensuite, vous avez un certain nombre d'entités (en fonction de la charge que vous devez gérer) qui ont leurs propres compteurs. Ces éclats de réserve des morceaux de id à partir de le maître et le servir hors de leur portée, jusqu'à épuisement de valeurs.

Algorithme rapide:

  1. Vous devez obtenir un numéro d'identification.
  2. Choisir un éclat au hasard.
  3. Si le fragment de départ est inférieure à sa fin, prendre du démarrage et de l'incrémenter.
  4. Si le fragment de départ est égal à (ou plus oh-oh) à sa fin, allez à la le maître, de prendre de la valeur et d'ajouter un montant n pour elle. Définir les éclats de commencer à la valeur récupérée plus un et à la fin de l'extrait, plus n.

Cela peut évoluer assez bien, cependant, le montant que vous pouvez être par le nombre de tessons multiplié par votre n de la valeur. Si vous voulez que vos enregistrements semble aller ce sera sans doute, mais si vous voulez avoir eux représentent le temps il ne sera pas exacte. Il est également important de noter que les valeurs les plus récentes peuvent avoir des trous, donc, si vous utilisez que pour scanner, pour quelque raison, vous avez à l'esprit les lacunes.

Modifier

J'ai besoin pour mon application (c'est pourquoi j'étais à la recherche à la question :P ) j'ai donc mis en œuvre ma solution. Elle peut saisir unique Id ainsi que de saisir efficacement les lots. Je l'ai testé dans un environnement contrôlé (sur appengine) et il s'est très bien comportée. Vous pouvez trouver le code sur github.

4voto

Ilian Iliev Points 1867

Jetez un oeil à la façon dont les compteurs éclatés sont fabriqués. Cela peut vous aider. De plus, avez-vous vraiment besoin qu'ils soient numériques. Si unique est satisfaisant, utilisez simplement les clés d'entité.

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