Skip to content

Commit eba1f19

Browse files
author
Chris Rossi
authored
fix: @non_transactional decorator was not working correctly with async (#554)
* fix: `@non_transactional` decorator was not working correctly with async Fixes #552 * We don't really need `_get_transaction` at all. In all cases, we know where to get the transaction from.
1 parent b4322d6 commit eba1f19

File tree

2 files changed

+60
-27
lines changed

2 files changed

+60
-27
lines changed

packages/google-cloud-ndb/google/cloud/ndb/_datastore_api.py

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ def lookup(key, options):
125125
"""
126126
context = context_module.get_context()
127127
use_datastore = context._use_datastore(key, options)
128-
in_transaction = bool(_get_transaction(options))
129-
if use_datastore and in_transaction:
128+
if use_datastore and options.transaction:
130129
use_global_cache = False
131130
else:
132131
use_global_cache = context._use_global_cache(key, options)
@@ -316,8 +315,7 @@ def get_read_options(options, default_read_consistency=None):
316315
ValueError: When ``read_consistency`` is set to ``EVENTUAL`` and there
317316
is a transaction.
318317
"""
319-
transaction = _get_transaction(options)
320-
318+
transaction = options.transaction
321319
read_consistency = options.read_consistency
322320

323321
if transaction is None:
@@ -332,27 +330,6 @@ def get_read_options(options, default_read_consistency=None):
332330
)
333331

334332

335-
def _get_transaction(options):
336-
"""Get the transaction for a request.
337-
338-
If specified, this will return the transaction from ``options``. Otherwise,
339-
it will return the transaction for the current context.
340-
341-
Args:
342-
options (_options.ReadOptions): The options for the request. Only
343-
``transaction`` will have any bearing here.
344-
345-
Returns:
346-
Union[bytes, NoneType]: The transaction identifier, or :data:`None`.
347-
"""
348-
transaction = getattr(options, "transaction", None)
349-
if transaction is None:
350-
context = context_module.get_context()
351-
transaction = context.transaction
352-
353-
return transaction
354-
355-
356333
@tasklets.tasklet
357334
def put(entity, options):
358335
"""Store an entity in datastore.
@@ -388,7 +365,7 @@ def put(entity, options):
388365
yield _cache.global_set(cache_key, cache_value, expires=expires)
389366

390367
if use_datastore:
391-
transaction = _get_transaction(options)
368+
transaction = context.transaction
392369
if transaction:
393370
batch = _get_commit_batch(transaction, options)
394371
else:
@@ -432,7 +409,7 @@ def delete(key, options):
432409
if use_global_cache:
433410
yield _cache.global_lock(cache_key)
434411

435-
transaction = _get_transaction(options)
412+
transaction = context.transaction
436413
if transaction:
437414
batch = _get_commit_batch(transaction, options)
438415
else:

packages/google-cloud-ndb/tests/system/test_misc.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,59 @@ def run(self):
399399
thread2.join()
400400

401401
assert activity["calls"] == 2
402+
403+
404+
@pytest.mark.usefixtures("client_context")
405+
def test_non_transactional_means_no_transaction(dispose_of):
406+
"""Regression test for #552
407+
408+
https://github.com/googleapis/python-ndb/issues/552
409+
"""
410+
N = 50
411+
412+
class SomeKind(ndb.Model):
413+
pass
414+
415+
class OtherKind(ndb.Model):
416+
pass
417+
418+
@ndb.tasklet
419+
def create_entities():
420+
parent_keys = yield [SomeKind().put_async() for _ in range(N)]
421+
422+
futures = []
423+
for parent_key in parent_keys:
424+
dispose_of(parent_key._key)
425+
futures.append(OtherKind(parent=parent_key).put_async())
426+
futures.append(OtherKind(parent=parent_key).put_async())
427+
428+
keys = yield futures
429+
for key in keys:
430+
dispose_of(key._key)
431+
432+
raise ndb.Return(keys)
433+
434+
@ndb.non_transactional()
435+
@ndb.tasklet
436+
def non_transactional_tasklet(keys):
437+
entities = yield ndb.get_multi_async(keys)
438+
raise ndb.Return(entities)
439+
440+
@ndb.non_transactional()
441+
@ndb.tasklet
442+
def also_a_non_transactional_tasklet():
443+
entities = yield OtherKind.query().fetch_async()
444+
raise ndb.Return(entities)
445+
446+
@ndb.transactional()
447+
def test_lookup(keys):
448+
entities = non_transactional_tasklet(keys).result()
449+
assert len(entities) == N * 2
450+
451+
@ndb.transactional()
452+
def test_query():
453+
return also_a_non_transactional_tasklet().result()
454+
455+
keys = create_entities().result()
456+
test_lookup(keys)
457+
eventually(test_query, length_equals(N * 2))

0 commit comments

Comments
 (0)