Google App Engine:查询在执行由 ndb.transaction 修饰的函数时如何进行交互
Google App Engine: How to do queries interact when they execute a function decorated by ndb.transaction
为了了解 ndb.transaction 的内部工作原理,我尝试了以下实验。
Udacity 示例中的会议 API 用于创建只有一个可用席位的会议。
我在处理 registerForConference API 的方法中添加了等待,如下所示。我记录调试消息以了解流程,如下所示
我开始了 registerForConference 的两次调用 API 一个接一个。
没有 ndb.transaction,两者都将 return 为真(即两个 return 都注册成功)。使用 ndb.transaction,第二个将 return 为假。所以,一切都按预期进行。
但是第二个请求所经历的步骤顺序出乎意料。我原以为第二个查询会在某个时候卡住,直到第一个查询完成,并且在尝试插入时会抛出 TransactionFailedError。相反,看起来第二个请求实际上通过该方法一次,然后 re-executes 在第一个请求完成后该方法。在第二个异常中,它读取 seatsAvailable 的更新值并确定它无法注册。这是预期的行为吗?如果是,这不是浪费吗,因为有些步骤可以并行完成,只有在第一个查询完成后才需要执行有冲突的步骤?
打印的调试日志消息序列
/_ah/spi/ConferenceApi.regForAConf
D 18:28:21.093 Checking for id_token.
D 18:28:21.093 id_token verification failed: Token is not an id_token (Wrong number of segments)
D 18:28:21.093 Checking for oauth token.
D 18:28:21.101 Returning user from matched oauth_user.
D 18:28:21.111 Entered conf registration check
D 18:28:21.125 Got a profile object
D 18:28:21.131 Got a conf object
D 18:28:21.131 Entered updating step
**Went through the entire method once**
**Then restarted after first API completed**
D 18:28:46.143 Leaving with exit value 1
D 18:28:46.168 Entered conf registration check
D 18:28:46.181 Got a profile object
D 18:28:46.187 Got a conf object
D 18:28:46.187 Transaction failed No seats available
D 18:28:46.187 Leaving with exit value 0
处理 API 请求的方法定义
@ndb.transactional(xg=True)
def _conferenceRegistration(self,confId):
#get conference from id
ret_val =True
user = endpoints.get_current_user()
if not user:
raise endpoints.UnauthorizedException('Authorization required')
user_id = getUserId(user)
logging.debug('Entered conf registration check')
p_key = ndb.Key(Profile, user_id)
prof = p_key.get()
logging.debug('Got a profile object')
conf_key = ndb.Key(urlsafe=confId)
conf = conf_key.get()
logging.debug('Got a conf object')
if conf and prof:
if conf.seatsAvailable>0:
logging.debug('Entered updating step')
conf.seatsAvailable=conf.seatsAvailable-1
time.sleep(25)
prof.conferencesToAttend.append(conf.name)
try:
conf.put()
except TransactionFailedError:
logging.debug('Transaction Failed error when trying to insert changes to conference')
ret_val=False
try:
prof.put()
except TransactionFailedError:
logging.debug('Transaction Failed error when trying to insert changes to profile')
ret_val=False
ret_val=True
else:
logging.debug('Transaction failed No seats available')
ret_val=False
else:
logging.debug('Could not get conf or profile instance')
ret_val=False
buf = 'Leaving with exit value %d' % (ret_val)
logging.debug(buf)
return BooleanMessage(regSucc=ret_val)
这是意料之中的。它并不总是处理交易的最有效方法,但这是他们选择使用的模型 -
它假设交易冲突很少见,并且只是在写入之前验证这一点。这被称为 'optimistic concurrency'。替代方案涉及锁定事物,这可能非常复杂,并且在交易不经常发生冲突时效率较低。
how transactions work on appengine might help explain more, or there's a wikkipedia page on optimistic concurrency control
的文档
为了了解 ndb.transaction 的内部工作原理,我尝试了以下实验。
Udacity 示例中的会议 API 用于创建只有一个可用席位的会议。
我在处理 registerForConference API 的方法中添加了等待,如下所示。我记录调试消息以了解流程,如下所示
我开始了 registerForConference 的两次调用 API 一个接一个。
没有 ndb.transaction,两者都将 return 为真(即两个 return 都注册成功)。使用 ndb.transaction,第二个将 return 为假。所以,一切都按预期进行。
但是第二个请求所经历的步骤顺序出乎意料。我原以为第二个查询会在某个时候卡住,直到第一个查询完成,并且在尝试插入时会抛出 TransactionFailedError。相反,看起来第二个请求实际上通过该方法一次,然后 re-executes 在第一个请求完成后该方法。在第二个异常中,它读取 seatsAvailable 的更新值并确定它无法注册。这是预期的行为吗?如果是,这不是浪费吗,因为有些步骤可以并行完成,只有在第一个查询完成后才需要执行有冲突的步骤?
打印的调试日志消息序列
/_ah/spi/ConferenceApi.regForAConf
D 18:28:21.093 Checking for id_token.
D 18:28:21.093 id_token verification failed: Token is not an id_token (Wrong number of segments)
D 18:28:21.093 Checking for oauth token.
D 18:28:21.101 Returning user from matched oauth_user.
D 18:28:21.111 Entered conf registration check
D 18:28:21.125 Got a profile object
D 18:28:21.131 Got a conf object
D 18:28:21.131 Entered updating step
**Went through the entire method once**
**Then restarted after first API completed**
D 18:28:46.143 Leaving with exit value 1
D 18:28:46.168 Entered conf registration check
D 18:28:46.181 Got a profile object
D 18:28:46.187 Got a conf object
D 18:28:46.187 Transaction failed No seats available
D 18:28:46.187 Leaving with exit value 0
处理 API 请求的方法定义
@ndb.transactional(xg=True)
def _conferenceRegistration(self,confId):
#get conference from id
ret_val =True
user = endpoints.get_current_user()
if not user:
raise endpoints.UnauthorizedException('Authorization required')
user_id = getUserId(user)
logging.debug('Entered conf registration check')
p_key = ndb.Key(Profile, user_id)
prof = p_key.get()
logging.debug('Got a profile object')
conf_key = ndb.Key(urlsafe=confId)
conf = conf_key.get()
logging.debug('Got a conf object')
if conf and prof:
if conf.seatsAvailable>0:
logging.debug('Entered updating step')
conf.seatsAvailable=conf.seatsAvailable-1
time.sleep(25)
prof.conferencesToAttend.append(conf.name)
try:
conf.put()
except TransactionFailedError:
logging.debug('Transaction Failed error when trying to insert changes to conference')
ret_val=False
try:
prof.put()
except TransactionFailedError:
logging.debug('Transaction Failed error when trying to insert changes to profile')
ret_val=False
ret_val=True
else:
logging.debug('Transaction failed No seats available')
ret_val=False
else:
logging.debug('Could not get conf or profile instance')
ret_val=False
buf = 'Leaving with exit value %d' % (ret_val)
logging.debug(buf)
return BooleanMessage(regSucc=ret_val)
这是意料之中的。它并不总是处理交易的最有效方法,但这是他们选择使用的模型 - 它假设交易冲突很少见,并且只是在写入之前验证这一点。这被称为 'optimistic concurrency'。替代方案涉及锁定事物,这可能非常复杂,并且在交易不经常发生冲突时效率较低。
how transactions work on appengine might help explain more, or there's a wikkipedia page on optimistic concurrency control
的文档