mirror of
https://github.com/apple/foundationdb.git
synced 2025-05-14 01:42:37 +08:00
python: Post-API Version 620, @fdb.transactional on a generator will throw.
Previously, writing code like @fdb.transactional def foo(tr): yield tr.get('a') yield tr.get('b') print(foo(db)) was accepted by the python bindings, but had surprising semantics. The function returned by @fdb.transactional opens a transaction, runs foo(), commits the transaction, and then returns the generator returned by foo(). This generator then uses the committed transaction. This worked before API version 410 (FDB 4.1), and hasn't worked since. This will also be a problem if a closure is returned from foo() that contains `tr`, but it's much harder to check that in Python. Rather than allow users to hit an unexpected and mysterious "Operation issued while a commit was outstanding" exception, it's nicer to explicitly highlight this problem as soon as we can. Unfortunately, we have no way to know that a function will return a generator until we call it, so that's the soonest we can give a more informative error.
This commit is contained in:
parent
6f52059750
commit
e8f994965d
@ -22,16 +22,17 @@
|
||||
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import datetime
|
||||
import functools
|
||||
import inspect
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import inspect
|
||||
import datetime
|
||||
import platform
|
||||
import os
|
||||
import sys
|
||||
import multiprocessing
|
||||
|
||||
import fdb
|
||||
from fdb import six
|
||||
|
||||
_network_thread = None
|
||||
@ -203,7 +204,9 @@ def transactional(*tr_args, **tr_kwargs):
|
||||
|
||||
It is important to note that the wrapped method may be called
|
||||
multiple times in the event of a commit failure, until the commit
|
||||
succeeds.
|
||||
succeeds. This restriction requires that the wrapped function
|
||||
may not be a generator, or a function that returns a closure that
|
||||
contains the `tr` object.
|
||||
|
||||
If given a Transaction, the Transaction will be passed into the
|
||||
wrapped code, and WILL NOT be committed at completion of the
|
||||
@ -247,7 +250,6 @@ def transactional(*tr_args, **tr_kwargs):
|
||||
except FDBError as e:
|
||||
yield asyncio.From(tr.on_error(e.code))
|
||||
else:
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if isinstance(args[index], TransactionRead):
|
||||
@ -269,6 +271,9 @@ def transactional(*tr_args, **tr_kwargs):
|
||||
except FDBError as e:
|
||||
tr.on_error(e.code).wait()
|
||||
|
||||
if fdb.get_api_version() >= 620 and isinstance(ret, types.GeneratorType):
|
||||
raise ValueError("Generators can not be wrapped with fdb.transactional")
|
||||
|
||||
# now = datetime.datetime.now()
|
||||
# td = now - last
|
||||
# elapsed = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6)
|
||||
|
@ -125,6 +125,16 @@ class Instruction:
|
||||
self.stack.push(self.index, val)
|
||||
|
||||
|
||||
def test_fdb_transactional_generator(db):
|
||||
try:
|
||||
@fdb.transactional
|
||||
def function_that_yields(tr):
|
||||
yield 0
|
||||
assert fdb.get_api_version() < 620, "Generators post-6.2.0 should throw"
|
||||
except ValueError as e:
|
||||
pass
|
||||
|
||||
|
||||
def test_db_options(db):
|
||||
db.options.set_max_watches(100001)
|
||||
db.options.set_datacenter_id("dc_id")
|
||||
|
Loading…
x
Reference in New Issue
Block a user