Hm... Combining decorators is a black art. Reversing the order is bad,
but I honestly can't tell without doing more pencil-and-paper
computations than I care to do whether what you do here is correct, so
I would write it out using ndb.transaction(callback) instead.
> 2. Is there a way to use ndb.transaction_async here instead of synchronous
> transaction? How could it improve performance?
Easy enough: take the above without the @ndb.transactional, and then
write another tasklet that invokes the transaction:
@ndb.tasklet
def transactional_wrapper_async(kind, kwargs_list):
res = yield ndb.transaction_async(lambda kind, kwargs_list:
update_entities_async(kind, kwargs_list))
raise ndb.Return(res)
(The lambda is necessary because transaction_async() only takes a
callback that needs no parameters; this is done so you can pass
keyword arguments to transaction_async() itself that affect the
transaction, e.g. xg=True or retries=1.)
It could only improve performance if you had some other unrelated
tasklet that could run in parallel with that. The other tasklet would
be unaffected by the transaction.
> 3. If there are a lot of entities to update, will all of them be locked for
> write during this transaction?
Yes, but unless they are all descendants of the same root key the
transaction will raise an error complaining about multiple entity
groups. When using the HR Datastore, you can use xg=True to have a
transaction that spans multiple entity groups, but it is still limited
to at most 5 entity groups.
--
--Guido van Rossum (python.org/~guido)
Right.
> I have come up with the following code, that should update many entities
> transactionally in parallel:
By creating many transactions, right? (One for each key.)
> @ndb.tasklet
> def update_entities_async(kind, kwargs_list):
>
> def run_txn(id, props):
> @ndb.tasklet
> def txn():
> entity = ndb.Key(kind, id).get()
This should be
entity = yield ndb.Key(kind, id).get_async()
> entity.populate(**props)
> key = yield entity.put_async()
> raise ndb.Return(key)
> return ndb.transaction_async(txn)
>
> res = yield [run_txn(props.pop('id'), props) for props in kwargs_list]
> raise ndb.Return(res)
>
> I haven't run this code on production yet, but it works on development
> server.
Otherwise looks good.
Fyi This happens to be issue 195.