Merge branch 'improve-perf' of github.com:kansi/bigchaindb into feat/memoize

This commit is contained in:
Vanshdeep Singh 2018-08-28 12:32:05 +02:00
commit 2a14878d94
4 changed files with 72 additions and 7 deletions

View File

@ -59,6 +59,7 @@ def create_transactions_secondary_index(conn, dbname):
# to query the transactions for a transaction id, this field is unique
conn.conn[dbname]['transactions'].create_index('id',
unique=True,
name='transaction_id')
# secondary index for asset uuid, this field is unique
@ -93,7 +94,7 @@ def create_assets_secondary_index(conn, dbname):
def create_blocks_secondary_index(conn, dbname):
conn.conn[dbname]['blocks']\
.create_index([('height', DESCENDING)], name='height')
.create_index([('height', DESCENDING)], name='height', unique=True)
def create_metadata_secondary_index(conn, dbname):

View File

@ -13,6 +13,8 @@ Attributes:
from collections import namedtuple
from copy import deepcopy
from functools import reduce
import functools
import ujson
import base58
from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256
@ -42,6 +44,55 @@ UnspentOutput = namedtuple(
)
def memoize(func):
cache = func.cache = {}
@functools.wraps(func)
def memoized_func(*args, **kwargs):
key = args[1]['id']
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoized_func
def memoize_class(func):
cache = func.cache = {}
@functools.wraps(func)
def memoized_func(*args, **kwargs):
key = args[0]._id
if key is None:
result = func(*args, **kwargs)
cache[result['id']] = result
return result
elif key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoized_func
def memoize_input_valid(func):
cache = func.cache = {}
@functools.wraps(func)
def memoized_func(*args, **kwargs):
inp_fulfillment = args[1].fulfillment
op = args[2]
msg = args[3]
key = '{}.{}.{}'.format(inp_fulfillment, op, msg)
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoized_func
class Input(object):
"""A Input is used to spend assets locked by an Output.
@ -500,7 +551,7 @@ class Transaction(object):
VERSION = '2.0'
def __init__(self, operation, asset, inputs=None, outputs=None,
metadata=None, version=None, hash_id=None):
metadata=None, version=None, hash_id=None, tx_dict=None):
"""The constructor allows to create a customizable Transaction.
Note:
@ -553,6 +604,7 @@ class Transaction(object):
self.outputs = outputs or []
self.metadata = metadata
self._id = hash_id
self.tx_dict = tx_dict
@property
def unspent_outputs(self):
@ -990,7 +1042,7 @@ class Transaction(object):
raise ValueError('Inputs and '
'output_condition_uris must have the same count')
tx_dict = self.to_dict()
tx_dict = self.tx_dict # self.to_dict()
tx_dict = Transaction._remove_signatures(tx_dict)
tx_dict['id'] = None
tx_serialized = Transaction._to_str(tx_dict)
@ -1003,6 +1055,7 @@ class Transaction(object):
return all(validate(i, cond)
for i, cond in enumerate(output_condition_uris))
@memoize_input_valid
def _input_valid(self, input_, operation, message, output_condition_uri=None):
"""Validates a single Input against a single Output.
@ -1048,6 +1101,7 @@ class Transaction(object):
ffill_valid = parsed_ffill.validate(message=message.digest())
return output_valid and ffill_valid
@memoize_class
def to_dict(self):
"""Transforms the object to a Python dictionary.
@ -1150,7 +1204,9 @@ class Transaction(object):
tx_body (dict): The Transaction to be transformed.
"""
# NOTE: Remove reference to avoid side effects
tx_body = deepcopy(tx_body)
# tx_body = deepcopy(tx_body)
# tx_body = rapidjson.loads(rapidjson.dumps(tx_body))
tx_body = ujson.loads(ujson.dumps(tx_body))
try:
proposed_tx_id = tx_body['id']
except KeyError:
@ -1167,6 +1223,7 @@ class Transaction(object):
raise InvalidHash(err_msg.format(proposed_tx_id))
@classmethod
@memoize
def from_dict(cls, tx, skip_schema_validation=True):
"""Transforms a Python dictionary to a Transaction object.
@ -1184,7 +1241,7 @@ class Transaction(object):
inputs = [Input.from_dict(input_) for input_ in tx['inputs']]
outputs = [Output.from_dict(output) for output in tx['outputs']]
return cls(tx['operation'], tx['asset'], inputs, outputs,
tx['metadata'], tx['version'], hash_id=tx['id'])
tx['metadata'], tx['version'], hash_id=tx['id'], tx_dict=tx)
@classmethod
def from_db(cls, bigchain, tx_dict_list):

View File

@ -124,7 +124,7 @@ class BigchainDB(object):
txn_metadatas = []
for transaction_obj in transactions:
# self.update_utxoset(transaction)
transaction = transaction_obj.to_dict()
transaction = transaction_obj.tx_dict
if transaction['operation'] == transaction_obj.CREATE:
asset = transaction.pop('asset')
asset['id'] = transaction['id']
@ -224,6 +224,13 @@ class BigchainDB(object):
return backend.query.delete_unspent_outputs(
self.connection, *unspent_outputs)
def is_commited(self, transaction_id):
transaction = backend.query.get_transaction(self.connection, transaction_id)
if transaction:
return True
else:
return False
def get_transaction(self, transaction_id):
transaction = backend.query.get_transaction(self.connection, transaction_id)

View File

@ -27,7 +27,7 @@ class Transaction(Transaction):
if self.operation == Transaction.CREATE:
duplicates = any(txn for txn in current_transactions if txn.id == self.id)
if bigchain.get_transaction(self.to_dict()['id']) or duplicates:
if bigchain.is_commited(self.id) or duplicates:
raise DuplicateTransaction('transaction `{}` already exists'
.format(self.id))