I have a NDB model that exposes a few instance methods to manipulate its state. In some request handlers, I need to call a few of these instance methods. In order to prevent calling put()
more than once on the same entity, the pattern I've used so far is similar to this:
class Foo(ndb.Model):
prop_a = ndb.StringProperty()
prop_b = ndb.StringProperty()
prop_c = ndb.StringProperty()
def some_method_1(self):
self.prop_a = "The result of some computation"
return True
def some_method_2(self):
if some_condition:
self.prop_b = "Some new value"
return True
return False
def some_method_3(self):
if some_condition:
self.prop_b = "Some new value"
return True
if some_other_condition:
self.prop_b = "Some new value"
self.prop_c = "Some new value"
return True
return False
def manipulate_foo(f):
updated = False
updated = f.some_method_1() or updated
updated = f.some_method_2() or updated
updated = f.some_method_3() or updated
if updated:
f.put()
Basically, each method that can potentially update the entity returns a bool to indicate if the entity has been updated and therefore needs to be saved. When calling these methods in sequence, I make sure to call put()
if any of the methods returned True
.
However, this pattern can be complex to implement in situations where other subroutines are involved. In that case, I need to make the updated boolean value returned from subroutines bubble up to the top-level methods.
I am now in the process of optimizing a lot of my request handlers, trying to limit as much as possibles the waterfalls reported by AppStat, using as much async APIs as I can and converting a lot of methods to tasklets.
This effort lead me to read the NDB Async documentation, which mentions that NDB implements an autobatcher which combines multiple requests in a single RPC call to the datastore. I understand that this applies to requests involving different keys, but does it also apply to redundant calls to the same entity?
In other words, my question is: could the above code pattern be replaced by this one?
class FooAsync(ndb.Model):
prop_a = ndb.StringProperty()
prop_b = ndb.StringProperty()
prop_c = ndb.StringProperty()
@ndb.tasklet
def some_method_1(self):
self.prop_a = "The result of some computation"
yield self.put_async()
@ndb.tasklet
def some_method_2(self):
if some_condition:
self.prop_b = "Some new value"
yield self.put_async()
@ndb.tasklet
def some_method_3(self):
if some_condition:
self.prop_b = "Some new value"
yield self.put_async()
elif some_other_condition:
self.prop_b = "Some new value"
self.prop_c = "Some new value"
yield self.put_async()
@ndb.tasklet
def manipulate_foo(f):
yield f.some_method_1()
yield f.some_method_2()
yield f.some_method_3()
Would all calls to put_async()
be combined into a single put
call on the entity? If yes, are there any caveats to using this approach vs sticking to manually checking for an updated return value and calling put
once at the end of the call sequence?