Pas*_*que 3 google-app-engine app-engine-ndb
我有一个NDB模型,它暴露了一些实例方法来操纵它的状态.在一些请求处理程序中,我需要调用其中一些实例方法.为了防止put()在同一个实体上多次调用,我到目前为止使用的模式与此类似:
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()
Run Code Online (Sandbox Code Playgroud)
基本上,每个可能更新实体的方法都会返回一个bool来指示实体是否已更新,因此需要保存.按顺序调用这些方法时,put()如果返回任何方法,我确保调用True.
但是,在涉及其他子例程的情况下,这种模式可能很复杂.在这种情况下,我需要使从子程序返回的更新的布尔值冒泡到顶级方法.
我现在正在优化我的许多请求处理程序,尝试尽可能多地限制AppStat报告的瀑布,使用尽可能多的异步API并将大量方法转换为tasklet.
这项工作使我开始阅读NDB异步文档,该文档提到NDB实现了一个autobatcher,它在单个RPC调用中将多个请求组合到数据存储区.我知道这适用于涉及不同密钥的请求,但它是否也适用于对同一实体的冗余调用?
换句话说,我的问题是:上面的代码模式可以被这个代替吗?
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()
Run Code Online (Sandbox Code Playgroud)
是否所有呼叫put_async()都会合并为一个put实体呼叫?如果是,是否有任何警告使用这种方法与坚持手动检查更新的返回值并put在调用序列结束时调用一次?
好吧,我咬了一下子弹并在一个测试GAE应用程序中测试了这3个场景,启用了AppStat来查看正在进行的RPC调用:
class Foo(ndb.Model):
prop_a = ndb.DateTimeProperty()
prop_b = ndb.StringProperty()
prop_c = ndb.IntegerProperty()
class ThreePutsHandler(webapp2.RequestHandler):
def post(self):
foo = Foo.get_or_insert('singleton')
foo.prop_a = datetime.utcnow()
foo.put()
foo.prop_b = str(foo.prop_a)
foo.put()
foo.prop_c = foo.prop_a.microsecond
foo.put()
class ThreePutsAsyncHandler(webapp2.RequestHandler):
@ndb.toplevel
def post(self):
foo = Foo.get_or_insert('singleton')
foo.prop_a = datetime.utcnow()
foo.put_async()
foo.prop_b = str(foo.prop_a)
foo.put_async()
foo.prop_c = foo.prop_a.microsecond
foo.put_async()
class ThreePutsTaskletHandler(webapp2.RequestHandler):
@ndb.tasklet
def update_a(self, foo):
foo.prop_a = datetime.utcnow()
yield foo.put_async()
@ndb.tasklet
def update_b(self, foo):
foo.prop_b = str(foo.prop_a)
yield foo.put_async()
@ndb.tasklet
def update_c(self, foo):
foo.prop_c = foo.prop_a.microsecond
yield foo.put_async()
@ndb.toplevel
def post(self):
foo = Foo.get_or_insert('singleton')
self.update_a(foo)
self.update_b(foo)
self.update_c(foo)
app = webapp2.WSGIApplication([
('/ndb-batching/3-puts', ThreePutsHandler),
('/ndb-batching/3-puts-async', ThreePutsAsyncHandler),
('/ndb-batching/3-puts-tasklet', ThreePutsTaskletHandler),
], debug=True)
Run Code Online (Sandbox Code Playgroud)
第一个,ThreePutsHandler显然最终会召唤Put3次.

但是,另外两个正在调用的测试put_async()最终只能调用Put:

所以我的问题的答案是:是的,冗余的ndb.Model.put_async()调用由NDB的自动调度功能进行批处理,最终作为单个datastore_v3.Put调用结束.这些put_async()调用是否在一个tasklet中进行并不重要.
关于在测试结果中观察到的数据存储区写操作数的说明:正如Shay在注释中指出的那样,每个修改的索引属性值有4个写入加上实体的1个写入.所以在第一次测试(3次连续put)中,我们观察到(4 + 1)*3 = 15次写操作.在另外两个测试(异步)中,我们观察到(4*3)+ 1 = 13个写操作.
因此,最重要的是,put_async对同一实体进行NDB批量多次调用可以通过单次调用数据存储来为我们节省大量延迟,并通过仅编写一次实体来节省一些写操作.
| 归档时间: |
|
| 查看次数: |
677 次 |
| 最近记录: |