The following below should create a Counter
model and use (deferred) tasks to increment the counter to 10. Visiting '/'
ought to create a single Counter
object with count = 10
. This happens in production. In development (localhost) multiple Counter
objects are created with the largest being 10:
I suspect this is because the put
is not synchronous on development (but appears to always be on production). Is there a way to make them synchronous?
Code snippet below:
class Counter(ndb.Model):
count = ndb.IntegerProperty(indexed=False)
def reset():
ndb.delete_multi(Counter().query().fetch(keys_only=True, use_cache=False, use_memcache=False))
def increment():
counter = Counter().query().get(use_cache=False, use_memcache=False)
if not counter:
counter = Counter(count=0)
counter.count += 1
counter.put()
if counter.count < 10:
deferred.defer(increment)
@app.route('/')
def hello():
"""Return a friendly HTTP greeting."""
reset()
deferred.defer(increment)
return 'Hello World!'
I have a git repo that reproduces this behavior here. You can find the commit that makes the last change here.
You should retrieve your counter by key and then you will avoid eventual consistency. Especially as you seem to only create a single Counter object. Not this won't scale if you have a large number of concurrent writes.
It would also pay to read the article linked to in the other answer. There a re number of problems with your approach.
Its seems odd to me that you would even consider using queries for this functionality. By specifying the key you will also guarantee a single counter entity.
The production 'synchronicity' is just apparent, it's not guaranteed (in your approach). It can always happen that a newly created counter is not found in the query, thus your code could create multiple counters.
More details in this Balancing Strong and Eventual Consistency with Google Cloud Datastore article.