Browse Source

session.py: introduce an lrucache for block tx hashes

Adjust request costing based on whether cache is hit.

Add stats to RPC getinfo
patch-2
Neil Booth 6 years ago
parent
commit
dcd255b3b6
  1. 60
      electrumx/server/session.py

60
electrumx/server/session.py

@ -116,6 +116,10 @@ class SessionManager(object):
self.txs_sent = 0
self.start_time = time.time()
self.history_cache = pylru.lrucache(256)
self._tx_hashes_cache = pylru.lrucache(1000)
self._tx_hashes_max_height = 0
self._tx_hashes_lookups = 0
self._tx_hashes_hits = 0
self.notified_height = None
self.hsub_results = None
# Masternode stuff only for such coins
@ -278,6 +282,7 @@ class SessionManager(object):
def _get_info(self):
'''A summary of server state.'''
cache_fmt = '{:,d} lookups {:,d} hits {:,d} entries'
return {
'closing': len([s for s in self.sessions if s.is_closing()]),
'daemon': self.daemon.logged_url(),
@ -293,6 +298,8 @@ class SessionManager(object):
'sessions': self.session_count(),
'sessions_with_subs': self.session_count_with_subs(),
'subs': self._sub_count(),
'tx_hashes_cache' : cache_fmt.format(
self._tx_hashes_lookups, self._tx_hashes_hits, len(self._tx_hashes)),
'txs_sent': self.txs_sent,
'uptime': util.formatted_time(time.time() - self.start_time),
'version': electrumx.version,
@ -536,6 +543,29 @@ class SessionManager(object):
return 0
return sum((group.cost() - session.cost) * group.weight for group in groups)
async def tx_hashes_at_blockheight(self, height):
'''Returns a pair (tx_hashes, cost).
tx_hashes is an ordered list of binary hashes, cost is an estimated cost of
getting the hashes; cheaper if in-cache. Raises RPCError.
'''
height = non_negative_integer(height)
self._tx_hashes_lookups += 1
tx_hashes = self._tx_hashes_cache.get(height)
if tx_hashes:
self._tx_hashes_hits += 1
return tx_hashes, 0.1 + len(tx_hashes) * 0.00004
try:
tx_hashes = await self.db.tx_hashes_at_blockheight(height)
except self.db.DBError as e:
raise RPCError(BAD_REQUEST, f'db error: {e!r}')
self._tx_hashes_cache[height] = tx_hashes
self._tx_hashes_max_height = max(height, self._tx_hashes_max_height)
return tx_hashes, 0.25 + len(tx_hashes) * 0.0001
def session_count(self):
'''The number of connections that we've sent something to.'''
return len(self.sessions)
@ -579,6 +609,12 @@ class SessionManager(object):
async def _notify_sessions(self, height, touched):
'''Notify sessions about height changes and touched addresses.'''
# Invalidate our tx hashes cache in case of a reorg
for key in range(height, self._tx_hashes_max_height + 1):
if key in self._tx_hashes_cache:
del self._tx_hashes_cache[key]
self._tx_hashes_max_height = height - 1
height_changed = height != self.notified_height
if height_changed:
await self._refresh_hsub_results(height)
@ -1154,27 +1190,13 @@ class ElectrumX(SessionBase):
self.bump_cost(1.0)
return await self.daemon_request('getrawtransaction', tx_hash, verbose)
async def _tx_hashes_at_blockheight(self, height):
'''Returns a pair (block_hash, tx_hashes) for the main chain block at
the given height.
Returns an ordered list of binary hashes.
'''
height = non_negative_integer(height)
try:
tx_hashes = await self.db.tx_hashes_at_blockheight(height)
except self.db.DBError as e:
raise RPCError(BAD_REQUEST, f'db error: {e!r}')
# Aim is to cost 3.0 for a 1MB block of 2,500 txs
self.bump_cost(0.5 + len(tx_hashes) / 2500)
return tx_hashes
def _get_merkle_branch(self, tx_hashes, tx_pos):
'''Return a merkle branch to a transaction as a list of hexadecimal strings.
tx_hashes: ordered list of binary tx hashes in a block
tx_pos: index of transaction in tx_hashes to create branch for
'''
self.bump_cost(len(tx_hashes) / 2500)
branch, _root = self.db.merkle.branch_and_root(tx_hashes, tx_pos)
return [hash_to_hex_str(hash) for hash in branch]
@ -1186,7 +1208,9 @@ class ElectrumX(SessionBase):
height: the height of the block it is in
'''
tx_hash_bytes = assert_tx_hash(tx_hash)
tx_hashes = await self._tx_hashes_at_blockheight(height)
tx_hashes, cost = await self.session_mgr.tx_hashes_at_blockheight(height)
self.bump_cost(cost)
try:
pos = tx_hashes.index(tx_hash_bytes)
except ValueError:
@ -1202,7 +1226,9 @@ class ElectrumX(SessionBase):
if merkle not in (True, False):
raise RPCError(BAD_REQUEST, f'"merkle" must be a boolean')
tx_hashes = await self._tx_hashes_at_blockheight(height)
tx_hashes, cost = await self.session_mgr.tx_hashes_at_blockheight(height)
self.bump_cost(cost)
try:
tx_hash = tx_hashes[tx_pos]
except IndexError:

Loading…
Cancel
Save