Problem: CI build fails when using memoized from_dict

Solution: Clear from_dict cache
This commit is contained in:
Vanshdeep Singh 2018-08-31 16:52:59 +02:00
parent 55c3fb5fb3
commit c2ea25860a
7 changed files with 41 additions and 25 deletions

View File

@ -91,10 +91,8 @@ def get_assets(conn, asset_ids):
@register_query(LocalMongoDBConnection) @register_query(LocalMongoDBConnection)
def get_spent(conn, transaction_id, output): def get_spent(conn, transaction_id, output):
query = {'inputs.fulfills': { query = {'inputs.fulfills': {'transaction_id': transaction_id,
'transaction_id': transaction_id,
'output_index': output}} 'output_index': output}}
return conn.run( return conn.run(
conn.collection('transactions') conn.collection('transactions')
.find(query, {'_id': 0})) .find(query, {'_id': 0}))

View File

@ -17,25 +17,41 @@ def memoize_from_dict(func):
@functools.wraps(func) @functools.wraps(func)
def memoized_func(*args, **kwargs): def memoized_func(*args, **kwargs):
if args[1].get('id', None):
args = list(args) args = list(args)
args[1] = HDict(args[1]) args[1] = HDict(args[1])
new_args = tuple(args) new_args = tuple(args)
return from_dict(func, *new_args, **kwargs) return from_dict(func, *new_args, **kwargs)
else:
return func(*args, **kwargs)
return memoized_func return memoized_func
class ToDictWrapper():
def __init__(self, tx):
self.tx = tx
def __eq__(self, other):
return self.tx.id == other.tx.id
def __hash__(self):
return hash(self.tx.id)
@lru_cache(maxsize=16384) @lru_cache(maxsize=16384)
def to_dict(func, *args, **kwargs): def to_dict(func, tx_wrapped):
return func(*args, **kwargs) return func(tx_wrapped.tx)
def memoize_to_dict(func): def memoize_to_dict(func):
@functools.wraps(func) @functools.wraps(func)
def memoized_func(*args, **kwargs): def memoized_func(*args, **kwargs):
if args[0].id: if args[0].id:
return to_dict(func, *args, **kwargs) return to_dict(func, ToDictWrapper(args[0]))
else: else:
return func(*args, **kwargs) return func(*args, **kwargs)

View File

@ -1012,7 +1012,6 @@ class Transaction(object):
return all(validate(i, cond) return all(validate(i, cond)
for i, cond in enumerate(output_condition_uris)) for i, cond in enumerate(output_condition_uris))
# @memoize_input_valid
@lru_cache(maxsize=16384) @lru_cache(maxsize=16384)
def _input_valid(self, input_, operation, message, output_condition_uri=None): def _input_valid(self, input_, operation, message, output_condition_uri=None):
"""Validates a single Input against a single Output. """Validates a single Input against a single Output.
@ -1063,7 +1062,7 @@ class Transaction(object):
def __hash__(self): def __hash__(self):
return hash(self.id) return hash(self.id)
@memoize_to_dict # @memoize_to_dict
def to_dict(self): def to_dict(self):
"""Transforms the object to a Python dictionary. """Transforms the object to a Python dictionary.
@ -1185,7 +1184,7 @@ class Transaction(object):
raise InvalidHash(err_msg.format(proposed_tx_id)) raise InvalidHash(err_msg.format(proposed_tx_id))
@classmethod @classmethod
@memoize_from_dict # @memoize_from_dict
def from_dict(cls, tx, skip_schema_validation=True): def from_dict(cls, tx, skip_schema_validation=True):
"""Transforms a Python dictionary to a Transaction object. """Transforms a Python dictionary to a Transaction object.

View File

@ -9,6 +9,7 @@ MongoDB.
import logging import logging
from collections import namedtuple from collections import namedtuple
from uuid import uuid4 from uuid import uuid4
import rapidjson
try: try:
from hashlib import sha3_256 from hashlib import sha3_256
@ -124,8 +125,7 @@ class BigchainDB(object):
assets = [] assets = []
txn_metadatas = [] txn_metadatas = []
for t in transactions: for t in transactions:
# self.update_utxoset(transaction) transaction = t.tx_dict if t.tx_dict else rapidjson.loads(rapidjson.dumps(t.to_dict()))
transaction = t.tx_dict if t.tx_dict else t.to_dict()
if transaction['operation'] == t.CREATE: if transaction['operation'] == t.CREATE:
asset = transaction.pop('asset') asset = transaction.pop('asset')
asset['id'] = transaction['id'] asset['id'] = transaction['id']

View File

@ -30,11 +30,11 @@ class ValidatorElection(Transaction):
CREATE = VALIDATOR_ELECTION CREATE = VALIDATOR_ELECTION
ALLOWED_OPERATIONS = (VALIDATOR_ELECTION,) ALLOWED_OPERATIONS = (VALIDATOR_ELECTION,)
def __init__(self, operation, asset, inputs, outputs, # def __init__(self, operation, asset, inputs, outputs,
metadata=None, version=None, hash_id=None): # metadata=None, version=None, hash_id=None, tx_):
# operation `CREATE` is being passed as argument as `VALIDATOR_ELECTION` is an extension # # operation `CREATE` is being passed as argument as `VALIDATOR_ELECTION` is an extension
# of `CREATE` and any validation on `CREATE` in the parent class should apply to it # # of `CREATE` and any validation on `CREATE` in the parent class should apply to it
super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id) # super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id)
@classmethod @classmethod
def get_validators(cls, bigchain, height=None): def get_validators(cls, bigchain, height=None):

View File

@ -13,7 +13,7 @@ from cryptoconditions import Ed25519Sha256
from pytest import mark, raises from pytest import mark, raises
from sha3 import sha3_256 from sha3 import sha3_256
pytestmark = mark.tendermint pytestmark = [mark.tendermint, mark.bdb]
def test_input_serialization(ffill_uri, user_pub): def test_input_serialization(ffill_uri, user_pub):
@ -533,7 +533,7 @@ def test_validate_input_with_invalid_parameters(utx):
input_conditions = [out.fulfillment.condition_uri for out in utx.outputs] input_conditions = [out.fulfillment.condition_uri for out in utx.outputs]
tx_dict = utx.to_dict() tx_dict = utx.to_dict()
tx_serialized = Transaction._to_str(tx_dict) tx_serialized = Transaction._to_str(tx_dict)
valid = utx._input_valid(utx.inputs[0], tx_serialized, input_conditions) valid = utx._input_valid(utx.inputs[0], tx_serialized, input_conditions[0])
assert not valid assert not valid

View File

@ -144,11 +144,15 @@ def _bdb(_setup_database, _configure_bigchaindb):
from bigchaindb import config from bigchaindb import config
from bigchaindb.backend import connect from bigchaindb.backend import connect
from .utils import flush_db from .utils import flush_db
from bigchaindb.common.memoize import to_dict, from_dict
conn = connect() conn = connect()
yield yield
dbname = config['database']['name'] dbname = config['database']['name']
flush_db(conn, dbname) flush_db(conn, dbname)
to_dict.cache_clear()
from_dict.cache_clear()
# We need this function to avoid loading an existing # We need this function to avoid loading an existing
# conf file located in the home of the user running # conf file located in the home of the user running
@ -256,8 +260,7 @@ def b():
def create_tx(alice, user_pk): def create_tx(alice, user_pk):
from bigchaindb.models import Transaction from bigchaindb.models import Transaction
name = f'I am created by the create_tx fixture. My random identifier is {random.random()}.' name = f'I am created by the create_tx fixture. My random identifier is {random.random()}.'
return Transaction.create([alice.public_key], [([user_pk], 1)], asset={'name': name})\ return Transaction.create([alice.public_key], [([user_pk], 1)], asset={'name': name})
.sign([alice.private_key])
@pytest.fixture @pytest.fixture