2016-10-28 4 views
0

Ich versuche gerade eine CustomUser-Entität in meinem App-Engine-Projekt zu erstellen, wenn sich ein Benutzer zum ersten Mal anmeldet. Ich möchte, dass CustomUser-Entitäten eindeutig sind, und ich möchte verhindern, dass dieselbe Entität mehr als einmal erstellt wird. Dies wäre ziemlich einfach zu tun, wenn ich es bei der Erstellung eines Entity mit einem Vorgänger versehen kann, da dies die Transaktion stark konsistent macht.So stellen Sie sicher, dass die Entität der App Engine ndb root gespeichert wird, bevor Sie mit dem restlichen Code fortfahren

Leider ist dies nicht der Fall, da eine CustomUser-Entität eine Root-Entität ist und daher letztendlich konsistent und nicht stark konsistent ist. Aus diesem Grund gibt es Fälle, in denen die Entität zweimal erstellt wird, was ich verhindern möchte, da dies später zu Problemen führen wird.

Die Frage ist also, gibt es eine Möglichkeit kann ich verhindern, dass die Entität mehr als einmal erstellt wird? Oder zumindest das Commit der Vorgängerinstanz stark konsistent machen, um Doppelungen zu vermeiden? Hier ist mein Code und eine Zwischenlösung (Hacky).

import time 
import logging 
from google.appengine.ext import ndb 

# sample Model 
class CustomUser(ndb.Model): 
    user_id = ndb.StringProperty(required=True) 
    some_data = ndb.StringProperty(required=True) 
    some_more_data = ndb.StringProperty(required=True) 


externally_based_user_id = "id_taken_from_somewhere_else" 
# check if this id already exists in the Model. 
# If it does not exist yet, create it 
user_entity = CustomUser.query(
    CustomUser.user_id == externally_based_user_id, 
    ancestor=None).get() 

if not user_entity: 
    # prepare the entity 
    user_entity = CustomUser(
     user_id=externally_based_user_id, 
     some_data="some information", 
     some_more_data="even more information", 
     parent=None 
    ) 
    # write the entity to ndb 
    user_key = user_entity.put() 
    # inform of success 
    logging.info("user " + str(user_key) + " created") 

    # eventual consistency workaround - loop and keep checking if the 
    # entity has already been created 
    # 
    # I understand that a while loop may not be the wisest solution. 
    # I can also use a for loop with n range to avoid going around the loop infinitely. 
    # Both however seem to be band aid solutions 
    while not entity_check: 
     entity_check = CustomUser.query(
      CustomUser.user_id == externally_based_user_id, 
      ancestor=None).get() 

     # time.sleep to prevent the instance from consuming too much processing power and 
     # memory, although I'm not certain if this has any real effect apart from 
     # reducing the number of loops 
     if not entity_check: 
      time.sleep(0.5) 

EDIT: Lösung landete ich basiert auf den beiden Daniel Roseman's suggestions zu verbrauchen. Dies kann durch die Verwendung von get_or_insert, wie von voscausa vorgeschlagen, weiter vereinfacht werden. Ich habe mich daran gewöhnt, die üblichen Methoden zu nutzen, um die Dinge klarer zu machen.

import logging 
from google.appengine.ext import ndb 


# ancestor Model 
# we can skip the creation of an empty class like this, and just use a string when 
# retrieving a key 
class PhantomAncestor(ndb.Model): 
    pass 


# sample Model 
class CustomUser(ndb.Model): 
    # user_id now considered redundance since we will be 
    # user_id = ndb.StringProperty(required=True) 
    some_data = ndb.StringProperty(required=True) 
    some_more_data = ndb.StringProperty(required=True) 


externally_based_user_id = "id_taken_from_somewhere_else" 

# construct the entity key using information we know. 
# entity_key = ndb.Key(*arbitrary ancestor kind*, *arbitrary ancestor id*, *Model*, *user_id*) 
# we can also use the string "PhantomAncestor" instead of passing in an empty class like so: 
# entity_key = ndb.Key("SomeRandomString", externally_based_user_id, CustomUser, externally_based_user_id) 
# check this page on how to construct a key: https://cloud.google.com/appengine/docs/python/ndb/keyclass#Constructors 
entity_key = ndb.Key(PhantomAncestor, externally_based_user_id, CustomUser, externally_based_user_id) 

# check if this id already exists in the Model. 
user_entity = entity_key.get() 

# If it does not exist yet, create it 
if not user_entity: 
    # prepare the entity with the desired key value 
    user_entity = CustomUser(
     # user_id=externally_based_user_id, 
     some_data="some information", 
     some_more_data="even more information", 
     parent=None, 
     # specify the custom key value here 
     id=externally_based_user_id 
    ) 
    # write the entity to ndb 
    user_key = user_entity.put() 
    # inform of success 
    logging.info("user " + str(user_key) + " created") 

# we should also be able to use CustomUser.get_and_insert to simplify the code further 
+0

Werfen Sie einen Blick auf get_or_insert. Es ist ein transnationaler Weg, eine einzigartige Einheit zu schaffen. – voscausa

+0

Danke, das scheint eine nette Abkürzung zu sein –

Antwort

2

Ein paar Dinge hier.

Beachten Sie zuerst, dass der Vorgänger nicht existieren muss. Wenn Sie eine stark konsistente Abfrage wünschen, können Sie einen beliebigen Schlüssel als Vorfahr verwenden. Eine zweite Option wäre user_id als Schlüssel. Dann können Sie einen Schlüssel get statt einer Abfrage, die wiederum stark konsistent ist.

+0

Danke, ich habe mir Ihre Vorschläge angesehen, und die Lösung, die ich am Ende implementiert habe, beinhaltet das Erstellen eines beliebigen Schlüssels für einen nicht existierenden Vorgänger und das Setzen dieses Schlüssels als Eltern beim Erstellen einer Entität. Indem ich dies tue, kann ich gets, puts und Abfragen stark konsistent machen, indem ich diesen Schlüssel als Vorfahre spezifiziere. Ich hole nun auch Entitäten mit einem Schlüssel aus 'user_id'. Der Code funktioniert jetzt gut und es werden keine doppelten Einträge erstellt. –

Verwandte Themen